6

How can I do the Ruby method "Flatten" Ruby Method in C#. This method flattens a jagged array into a single-dimensional array.

For example:

s = [ 1, 2, 3 ]           #=> [1, 2, 3]
t = [ 4, 5, 6, [7, 8] ]   #=> [4, 5, 6, [7, 8]]
a = [ s, t, 9, 10 ]       #=> [[1, 2, 3], [4, 5, 6, [7, 8]], 9, 10]
a.flatten                 #=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10
1
  • You are dealing with a jagged (array of array) arrays here, and not multidimensional ones. Commented Oct 13, 2008 at 9:21

4 Answers 4

12

Recursive solution:

IEnumerable Flatten(IEnumerable array)
{
    foreach(var item in array)
    {
        if(item is IEnumerable)
        {
            foreach(var subitem in Flatten((IEnumerable)item))
            {
                yield return subitem;
            }
        }
        else
        {
            yield return item;
        }
    }
}

EDIT 1:

Jon explains in the comments why it cannot be a generic method, take a look!

EDIT 2:

Matt suggested making it an extension method. Here you go, just replace the first line with:

public static IEnumerable Flatten(this IEnumerable array)

and you can use it like this:

foreach(var item in myArray.Flatten()) { ... }
Sign up to request clarification or add additional context in comments.

9 Comments

My initial thought was "Why isn't it generic?" - but of course it can't be because only very rarely is an iterable version of T also a T. (e.g. IEnumerable<object> is still an object, but IEnumerable<string> isn't a string.) It may be worth clarifying this in the answer.
Also I'm not sure that there's any way to declare a strongly-typed ragged array in C# is there? It has to be object[], which means IEnumerable is a reasonable parameter type for this method.
Matt: Not sure what you mean by "strongly-typed ragged array" but int[][] and int[,] (for jagged and rectangular arrays of ints respectively) are fine.
@Matt: Indeed, that's exactly what I thought.
@Jon: I always try to do the easiest approach that works first. Apparently it's also the most generic: int[][] and int[,] are both IEnumerables. If the caller of flatten() knows the types in advance she can always cast to them.
|
2

I would have responded in a comment, but I need more than 300 characters.

@Alexander's solution is awesome, but it runs into a problem with arrays of strings. Since string implements IEnumerable, I think it will end up returning each character in every string. You can use a generic parameter to tell it what kind of thing you are hoping to have returned in these cases, e.g.:

public static IEnumerable Flatten<T>(IEnumerable e)
{
    if (e == null) yield break;
    foreach (var item in e)
    {
        if (item is T)
           yield return (T)item;
        else if (item is IEnumerable)
        {
            foreach (var subitem in Flatten<T>((IEnumerable)item))
                yield return subitem;
        }
        else
           yield return item;
    }
}

Comments

2

Couldn't you just use IEnumerable#SelectMany?

Comments

0

I would also agree that SelectMany does this.

var s = new[] { 1, 2, 3 };
var t = new[] { 4, 5, 6 };
var a = new[] { s, t, new[] {7, 8} };

a.SelectMany(e => e); 
// IEnumerable<int> {1,2,3,4,5,6,7,8}

I normalized the array. s and t are arrays of int. a is an array of arrays. You don't have mixed arrays with int and arrays, like you can do in ruby.

But vice-versa. flatten is the ruby equivalent I was looking for SelectMany in C#

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.