合并多个列表

鉴于我有一个列表列表, List <List> ,其中所有列表包含0个或更多项,可能但不一定都是相同的数字。 我希望有一个包含列表中所有项目的列表…但我希望订单如下:首先是所有列表中的第一个项目,以便它们出现在“超级列表”中。

防爆。

 List[0] = { 'Apple', 'Blueberry', 'Cranberry' } List[1] = { 'Anteater', 'Baboon', 'Camel', 'Dodo'} List[2] = { 'Albatross', 'Blackbird', 'Chicken'} result = { 'Apple', 'Anteater', 'Albatross', 'Blueberry', 'Baboon', 'Blackbird', 'Cranberry', 'Camel', 'Chicken', 'Dodo' } 

(请注意,这不是按字母顺序排列的,Blueberry出现在Baboon之前)

当然,只要有一个非空的列表,我就可以通过计数器循环遍历’superlist’,逐个将项添加到结果列表中:

 int i = 0; bool done = false; while (!done) { bool found = false; foreach (list l in superlist) { if (l.Count() > i) { found = true; result.Add(l[i]); } } i++; if (!found) done = true; } 

但是使用一些优化的LINQ函数来做这件事会更好。 我一直在研究Zip , GroupBy和Aggregate ,但无法让它们工作。

那么:是否有一个漂亮的LINQ函数,或多个的组合,将其变成漂亮的代码,或者我应该坚持(并可能优化)我当前的函数?

编辑:一个简单的SelectMany(x => x)也没有这个技巧,因为它保留了列表的顺序,而不是像我的算法那样折叠它们。 有关详细信息,请参阅我的初始问题

你需要SelectMany

 var result = lists.SelectMany(x => x.Select((s, inx) => new { s, inx })) .GroupBy(x => x.inx) .SelectMany(x => x.Select(y => ys)) .ToList(); 

编辑

对于那些想要尝试的人,初始化代码。

 List> lists = new List>() { new List(){ "Apple", "Blueberry", "Cranberry" }, new List(){ "Anteater", "Baboon", "Camel", "Dodo"}, new List(){ "Albatross", "Blackbird", "Chicken"}, }; 

编辑2

产量: Apple,Anteater,Albatross,Blueberry,Baboon,Blackbird,Cranberry,Camel,Chicken,Dodo

只需使用MyListOfLists.SelectMany(x => x).OrderByDescending(x => x).ToList()

SelectMany会将您的列表展平为一个。 OrderByDescending对该结果进行操作,并将结果按字母顺序排列(我认为您想要)。 然后调用ToList强制执行并获取List而不是IEnumerable

我知道我已经迟到了,但是由于这篇文章已被我从另一篇文章中引用作为解决问题的基础,我觉得有必要在这个主题上添加一些东西。

问题中有两个相互矛盾的陈述:

但是使用一些优化的LINQ函数来做这件事会更好。

然后

那么:是否有一个漂亮的LINQ函数,或多个的组合,将其变成漂亮的代码,或者我应该坚持(并可能优化)我当前的函数?

优化漂亮之间有很大的区别。 当然,这取决于“优化”的含义。 漂亮,可读,较短的代码可能被认为是针对维护进行了优化,但大部分时间都不是为了提高性能。 所以说说性能。 接受的答案可能看起来很酷,但是时间和空间效率都很低(由于分组依据),甚至不提供类似“标准LINQ” Concat函数的延迟执行行为。

在这方面,由OP提供的原始function要好得多。 但是我们可以进一步创建一个更通用的函数(不需要列表),它可以有效地完成所有这些,同时遵循LINQ 实现精神:

 static class Extensions { public static IEnumerable Merge(this IEnumerable> source) { var queue = new Queue>(); IEnumerator itemEnumerator = null; try { // First pass: yield the first element (if any) and schedule the next (if any) foreach (var list in source) { itemEnumerator = list.GetEnumerator(); if (!itemEnumerator.MoveNext()) itemEnumerator.Dispose(); else { yield return itemEnumerator.Current; if (itemEnumerator.MoveNext()) queue.Enqueue(itemEnumerator); else itemEnumerator.Dispose(); } } // Second pass: yield the current element and schedule the next (if any) while (queue.Count > 0) { itemEnumerator = queue.Dequeue(); yield return itemEnumerator.Current; if (itemEnumerator.MoveNext()) queue.Enqueue(itemEnumerator); else itemEnumerator.Dispose(); } } finally { if (itemEnumerator != null) itemEnumerator.Dispose(); while (queue.Count > 0) queue.Dequeue().Dispose(); } } } 

请注意,通过消除重复部分并合并两个通道,我可以很容易地缩短它,但这会使它更不易读,也不会更快(实际上由于需要一些额外的检查,它会慢一点)。

总结:库(可重用)代码不必(并且在大多数情况下)不是酷或短。 如果它可以,很好,但这不应该是驱动程序。

使用Enumerable.SelectMany展平列表中的项目并从中创建一个新列表。

 var result = superlist.SelectMany(r=> r).ToList(); 

如果列表包含不同的项目并且列表中没有重复的项目,则此方法有效。

 var result = list.SelectMany(x=>x) .OrderBy(x=>list.First(a=>a.Contains(x)) .IndexOf(x)); 

没有简单或最优(通过性能和代码可读性)的方式如何从所有列表中选择第一个元素,然后是第二个,具有内置的linq扩展。 但是,您可以从代码中构建自己的扩展,或者如果您不喜欢while循环,如下所示:

 public static class MyLinqExtensions { public static void MySelect(this IEnumerable> superlist) { int index = 0; foreach (IEnumerable list in superlist) { if (index < list.Count()) { yield return list.ElementAt(index); } index++; } } } 

最后,您可以在结果上调用Enumerable.Distinct()以获取唯一值。

我明白你要做什么:

 var result = input.SelectMany(l => l.Select((o, i) => new { o, i })).GroupBy(o => oi).OrderBy(g => g.Key).SelectMany(g => g.Select(o => oo);