“转动”IEnumerable <IEnumerable > 90度

我正在寻找的是一个基本的操作(我肯定有一个名字,我只是没有意识到atm)。 我有一个矩阵像:

{1,2,3}

{A,N,F}

{7,8,9}

我想改变它

{1,A,7}

{2,N,8}

{3,F,9}

(以上只是对象的标识符而不是实际值。实际对象属于同一类型且无序)

我更喜欢它的声明性解决方案,但速度是一个因素。 我将不得不转动几张桌子(每分钟100k格),慢速版本将在关键路径上。

但是我对可读解决方案仍然更感兴趣。 我正在寻找下面的替代解决方案。 (换句话说,我不是指变化,而是一种不同的方法)

var arrays = rows.Select(row => row.ToArray()); var cellCount = arrays.First().Length; for(var i = 0;i<cellCount;i++){ yield return GetRow(i,arrays); } IEnumerable GetRow(int i,IEnumerable rows){ foreach(var row in rows}{ yield return row[i]; } } 

在两个几乎同样可读的解决方案中,我会更快,但可读性在速度之前

编辑它将始终是一个方阵

我对这个实现有点不确定。 它具有迭代器本地的副作用,但对我来说看起来很干净。 这假定每个序列长度相同但应该适用于任何序列。 您可以将其视为可变长度的Zip()方法。 它应该比其他答案中找到的其他链接LINQ解决方案表现更好,因为它只使用工作所需的最少操作。 如果不使用LINQ,可能会更好。 甚至可能被认为是最佳的。

 public static IEnumerable> Transpose(this IEnumerable> source) { if (source == null) throw new ArgumentNullException("source"); var enumerators = source.Select(x => x.GetEnumerator()).ToArray(); try { while (enumerators.All(x => x.MoveNext())) { yield return enumerators.Select(x => x.Current).ToArray(); } } finally { foreach (var enumerator in enumerators) enumerator.Dispose(); } } 

只是快速的谷歌搜索揭示了这些解决方案:

看看这个扩展方法。 Linq转置 。 我不确定性能,但代码看起来很优雅。

您的问题似乎暗示您要修改原始矩阵。

如果是这种情况,并且如果您能够将矩阵存储为IList> matrix ,那么这将起作用,但是,仅在方阵矩阵的情况下。

 for(int i = 0; i < matrix.Count; ++i) { for(int j = 0; j < i; ++j) { T temp = matrix[i][j]; matrix[i][j] = matrix[j][i]; matrix[j][i] = temp } } 

那么,你在这里寻找的是转换T[][] -> T[][] 。 有很多IEnumerabe>.Transpose()解决方案,但它们都归结为使用临时查找/键循环枚举,并且它们在大量的性能上留下了很多不足之处。 你的例子实际上工作得更快(虽然你也可以松开第二个foreach)。

首先问“我真的需要LINQ”。 你还没有描述转置矩阵的目的是什么,如果速度确实是你关心的话,你可以做好远离LINQ / foreach并以旧式方式(对于内部)来做

如果有人有兴趣,这是我的。 它以与Jeff相同的方式执行,但似乎稍微快一些(假设那些ToArrays()是必要的)。 没有可见的循环或临时,它更紧凑:

 public static IEnumerable> Transpose( this IEnumerable> source) { return source .Select(a => a.Select(b => Enumerable.Repeat(b, 1))) .Aggregate((a, b) => a.Zip(b, Enumerable.Concat)); } 

如果你还需要它来处理空列表,那么它就变成了:

 public static IEnumerable> Transpose( this IEnumerable> source) { return source .Select(a => a.Select(b => Enumerable.Repeat(b, 1))) .DefaultIfEmpty(Enumerable.Empty>()) .Aggregate((a, b) => a.Zip(b, Enumerable.Concat)); } 

我注意到提问者写道矩阵总是方形的。 这个实现(和jeffs)将一次评估整行,但如果我们知道矩阵是方形的,我们可以用更合适的方式重写zip函数:

 public static IEnumerable> Transpose( this IEnumerable> source) { return source .Select(a => a.Select(b => Enumerable.Repeat(b, 1))) .DefaultIfEmpty(Enumerable.Empty>()) .Aggregate(Zip); } public static IEnumerable> Zip( IEnumerable> first, IEnumerable> second) { var firstEnum = first.GetEnumerator(); var secondEnum = second.GetEnumerator(); while (firstEnum.MoveNext()) yield return ZipHelper(firstEnum.Current, secondEnum); } private static IEnumerable ZipHelper( IEnumerable firstEnumValue, IEnumerator> secondEnum) { foreach (var item in firstEnumValue) yield return item; secondEnum.MoveNext(); foreach (var item in secondEnum.Current) yield return item; } 

这样,每个元素在返回之前都不会被评估。