Как вы переносите измерения в 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>
.).
Ответы
Ответ 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]));