Created
February 5, 2019 03:15
-
-
Save msmolcic/fa44f2e6daa99b29080c9a9d31e92a87 to your computer and use it in GitHub Desktop.
Nested arrays problem
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Collections; | |
using System.Collections.Generic; | |
using System.Linq; | |
namespace NestedArrays | |
{ | |
public static class Extensions | |
{ | |
/// <summary> | |
/// Flattens the structure of the input of the indefinite depth into | |
/// the array of integers found while traversing through the input. | |
/// </summary> | |
/// <remarks> | |
/// All non-integer values will be skipped while traversing. | |
/// Returns an empty array when the input is null. | |
/// </remarks> | |
/// <param name="input">Input values containing anything.</param> | |
/// <returns>Flattened array of integers.</returns> | |
public static int[] Flatten(this IEnumerable<object> input) | |
{ | |
return Traverse(input).ToArray(); | |
} | |
/// <summary> | |
/// Traverses through the input and returns all integers found. | |
/// </summary> | |
/// <remarks> | |
/// Using Stack data structure internally instead of recursive | |
/// calls in order to avoid StackOverflowException. | |
/// </remarks> | |
/// <param name="input">Input values containing anything.</param> | |
/// <returns>All integers found in the input.</returns> | |
private static IEnumerable<int> Traverse(IEnumerable<object> input) | |
{ | |
if (input == null) | |
yield break; | |
// Stack will contain all the items that are yet to be processed. | |
var stack = new Stack<IEnumerable<object>>(); | |
stack.Push(input); | |
while (stack.Any()) | |
{ | |
var next = stack.Pop().ToArray(); | |
for (int index = 0; index < next.Length; ++index) | |
{ | |
var item = next[index]; | |
if (item is IEnumerable items) | |
{ | |
// If there are remaining items to process after the current item, | |
// add them to the stack before new items. That way we're keeping | |
// items in the same order they were in the input. | |
if (index + 1 < next.Length) | |
stack.Push(next.Skip(index + 1)); | |
stack.Push(items.Cast<object>()); | |
break; | |
} | |
if (item is int number) | |
yield return number; | |
} | |
} | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using Xunit; | |
namespace NestedArrays | |
{ | |
public class ExtensionsTest | |
{ | |
[Fact] | |
public void Flatten_NestedArraySetToNull_ReturnsEmptyArray() | |
{ | |
// Arrange | |
List<object> nestedArray = null; | |
// Act | |
int[] result = nestedArray.Flatten(); | |
// Assert | |
Assert.Empty(result); | |
} | |
[Fact] | |
public void Flatten_NestedArrayHugeDepth_DoesNotThrowStackOverflowException() | |
{ | |
// Arrange | |
const int DepthLevel = 100000; | |
var nestedArray = new List<object>(); | |
var childArray = new List<object>(); | |
nestedArray.Add(childArray); | |
for (int index = 0; index < DepthLevel; index++) | |
{ | |
var newList = new List<object>() { index + 1 }; | |
childArray.Add(newList); | |
childArray = newList; | |
} | |
int[] expectedResult = Enumerable.Range(1, DepthLevel).ToArray(); | |
// Act | |
int[] result = nestedArray.Flatten(); | |
// Assert | |
Assert.Equal(expectedResult, result); | |
} | |
[Fact] | |
public void Flatten_NestedArrayWithMixedElements_ReturnsOnlyIntegersInTheCorrectOrder() | |
{ | |
// Arrange | |
var nestedArray = new List<object>() | |
{ | |
null, 1, | |
new DateTime(2000, 10, 10), | |
2, false, 'a', | |
new List<object>() | |
{ | |
3, 4, | |
new List<int>() | |
{ | |
5, 6 | |
}, | |
new List<decimal>() | |
{ | |
3.14m, | |
6.22m, | |
4m | |
}, | |
new Dictionary<int, int>() | |
{ | |
{ 1, 1 }, | |
{ 2, 2 } | |
}, | |
new List<object>() | |
{ | |
null, "Irrelevant string", 7, 8, | |
new int[] { 9 } | |
}, | |
10, 11 | |
}, | |
12, 13, null, true, 14, 15, | |
new Func<int, int>((num) => num + 2) | |
}; | |
int[] expectedResult = Enumerable.Range(1, 15).ToArray(); | |
// Act | |
int[] result = nestedArray.Flatten(); | |
// Assert | |
Assert.Equal(expectedResult, result); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment