Подтвердить что ты не робот

Как вы переносите измерения в 2D-коллекции с помощью LINQ?

Рассмотрим следующую структуру:

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

Как я могу перечислить эту коллекцию, чтобы получить коллекции IEnumerable<int>, состоящие из первых элементов, вторых элементов и т.д.?

То есть {1, 4, 7}, {2, 5, 8},...

(Хотя реализация, которую я выбрал, является int[] объектами, предположим, что у вас есть только функциональность IEnumerable<int>.).

4b9b3361

Ответ 1

Здесь используется подход, в котором вместо рекурсии используется генератор. Там меньше конструкции массива, так что это может быть быстрее, но это полная гипотеза.

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());
    }
}

Ответ 2

Код кредита идет здесь (непроверенный, но выглядит хорошо).

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();     

Ответ 3

Если все элементы гарантированно имеют одинаковую длину, вы можете сделать это:

IEnumerable<IEnumerable<int>> Transpose(IEnumerable<IEnumerable<int>> collection)
{
    var width = collection.First().Count();
    var flattened = collection.SelectMany(c => c).ToArray();
    var height = flattened.Length / width;
    var result = new int[width][];

    for (int i = 0; i < width; i++)
    {
        result[i] = new int[height];
        for (int j = i, k = 0; j < flattened.Length; j += width, k++)
            result[i][k] = flattened[j];
    }

    return result;
}

Ответ 4

Только мои 2 цента В чистом linq:

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

Или с некоторыми inpurity:

var r1 = collection.First().Select((frow, i) => collection.Select(row => row.ToArray()[i]));