如何“压缩”或“旋转”可变数量的列表?

如果我有一个包含任意数量列表的列表,如下所示:

var myList = new List<List>() { new List() { "a", "b", "c", "d" }, new List() { "1", "2", "3", "4" }, new List() { "w", "x", "y", "z" }, // ...etc... }; 

…有没有办法以某种方式将列表“拉链”或“旋转”成这样的东西?

 { { "a", "1", "w", ... }, { "b", "2", "x", ... }, { "c", "3", "y", ... }, { "d", "4", "z", ... } } 

显而易见的解决方案是做这样的事情:

 public static IEnumerable<IEnumerable> Rotate(this IEnumerable<IEnumerable> list) { for (int i = 0; i  x.Count()); i++) { yield return list.Select(x => x.ElementAt(i)); } } // snip var newList = myList.Rotate(); 

…但我想知道是否有更清洁的方式,使用linq或其他方式?

您可以滚动自己的ZipMany实例,该实例手动迭代每个枚举。 在投影每个序列后,这可能比使用GroupBy序列在更大的序列上表现更好:

 public static IEnumerable ZipMany( IEnumerable> source, Func, TResult> selector) { // ToList is necessary to avoid deferred execution var enumerators = source.Select(seq => seq.GetEnumerator()).ToList(); try { while (true) { foreach (var e in enumerators) { bool b = e.MoveNext(); if (!b) yield break; } // Again, ToList (or ToArray) is necessary to avoid deferred execution yield return selector(enumerators.Select(e => e.Current).ToList()); } } finally { foreach (var e in enumerators) e.Dispose(); } } 

您可以使用Select扩展名Func来执行此操作:

 var rotatedList = myList.Select(inner => inner.Select((s, i) => new {s, i})) .SelectMany(a => a) .GroupBy(a => ai, a => as) .Select(a => a.ToList()).ToList(); 

这将为您提供另一个List>

分解

 .Select(inner => inner.Select((s, i) => new {s, i})) 

对于每个内部列表,我们将列表的内容投影到具有两个属性的新匿名对象: s ,字符串值,以及i原始列表中该值的索引。

 .SelectMany(a => a) 

我们将结果展平为一个列表

 .GroupBy(a => ai, a => as) 

我们通过匿名对象的i属性进行分组(回想一下这是索引)并选择s属性作为我们的值(仅限字符串)。

 .Select(a => a.ToList()).ToList(); 

对于每个组,我们将可枚举更改为列表,并将所有组的另一个列表更改为。

如何将SelectManyGroupBy与某些索引一起使用?

 // 1. Project inner lists to a single list (SelectMany) // 2. Use "GroupBy" to aggregate the item's based on order in the lists // 3. Strip away any ordering key in the final answer var query = myList.SelectMany( xl => xl.Select((vv,ii) => new { Idx = ii, Value = vv })) .GroupBy(xx => xx.Idx) .OrderBy(gg => gg.Key) .Select(gg => gg.Select(xx => xx.Value)); 

来自LinqPad:

我们分组物品

这是基于Matrix Transposition的低效变体:

 public static class Ext { public static IEnumerable> Rotate( this IEnumerable> src) { var matrix = src.Select(subset => subset.ToArray()).ToArray(); var height = matrix.Length; var width = matrix.Max(arr => arr.Length); T[][] transpose = Enumerable .Range(0, width) .Select(_ => new T[height]).ToArray(); for(int i=0; i 

看看codeplex上的linqlib项目 ,它有一个旋转function,可以完全满足你的需要。

您可以使用Range压缩for循环:

 var result = Enumerable.Range(0, myList.Min(l => l.Count)) .Select(i => myList.Select(l => l[i]).ToList()).ToList(); 
 (from count in Range(myList[0].Count) select new List( from count2 in Range(myList.Count) select myList[count2][count]) ).ToList(); 

它不漂亮,但我认为它会起作用。