Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you transpose dimensions in a 2D collection using LINQ?

Tags:

c#

linq

Consider the following structure:

IEnumerable<IEnumerable<int>> collection = new[] { 
    new [] {1, 2, 3}, 
    new [] {4, 5, 6}, 
    new [] {7, 8, 9} 
};

How can I enumerate this collection so that I obtain IEnumerable<int> collections made up of the first items, second items, etc.?

That is, {1, 4, 7}, {2, 5, 8}, ...

(Though the implementation I've chosen is int[] objects, assume you only have IEnumerable<int> functionality. Thanks.)

like image 856
jeebs Avatar asked May 11 '12 16:05

jeebs


4 Answers

Here's an approach that uses a generator instead of recursion. There's less array construction too, so it might be faster, but that's totally conjecture.

public static IEnumerable<IEnumerable<T>> Transpose<T>(
    this IEnumerable<IEnumerable<T>> @this) 
{
    var enumerators = @this.Select(t => t.GetEnumerator())
                           .Where(e => e.MoveNext());

    while (enumerators.Any()) {
        yield return enumerators.Select(e => e.Current);
        enumerators = enumerators.Where(e => e.MoveNext());
    }
}
like image 161
recursive Avatar answered Oct 17 '22 21:10

recursive


Just my 2 cents In pure linq:

 var transpond =           collection.First().Select((frow,i)=>collection.Select(row=>row.ElementAt(i)));

Or with some inpurity:

var r1 = collection.First().Select((frow, i) => collection.Select(row => row.ToArray()[i]));
like image 40
Val Bakhtin Avatar answered Oct 17 '22 20:10

Val Bakhtin


Code credit goes here (untested but looks fine).

public static class LinqExtensions
{
    public static IEnumerable<IEnumerable<T>> Transpose<T>(this IEnumerable<IEnumerable<T>> values)
    {
        if (!values.Any()) 
            return values;
        if (!values.First().Any()) 
            return Transpose(values.Skip(1));

        var x = values.First().First();
        var xs = values.First().Skip(1);
        var xss = values.Skip(1);
        return
         new[] {new[] {x}
           .Concat(xss.Select(ht => ht.First()))}
           .Concat(new[] { xs }
           .Concat(xss.Select(ht => ht.Skip(1)))
           .Transpose());
    }
}
//Input: transpose [[1,2,3],[4,5,6],[7,8,9]]
//Output: [[1,4,7],[2,5,8],[3,6,9]]
var result = new[] {new[] {1, 2, 3}, new[] {4, 5, 6}, new[] {7, 8, 9}}.Transpose();     
like image 3
ChristopheD Avatar answered Oct 17 '22 19:10

ChristopheD


Assuming all the sequences are of the same length.

static void Main(string[] args)
{
    IEnumerable<IEnumerable<int>> collection =
        new[]
        {
            new [] {1, 2, 3},
            new [] {4, 5, 6 },
            new [] {7, 8, 9}
        };
    Console.WriteLine("\tInitial");
    Print(collection);

    var transposed =
        Enumerable.Range(0, collection.First().Count())
                  .Select(i => collection.Select(j => j.ElementAt(i)));
    Console.WriteLine("\tTransposed");
    Print(transposed);
}

static void Print<T>(IEnumerable<IEnumerable<T>> collection)=>
    Console.WriteLine(string.Join(Environment.NewLine, collection.Select(i => string.Join(" ", i))));

Gives:

        Initial
1 2 3
4 5 6
7 8 9
        Transposed
1 4 7
2 5 8
3 6 9
like image 1
Alex Avatar answered Oct 17 '22 20:10

Alex