合并多个列表

时间:2013-08-23 18:46:13

标签: c# .net linq list

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

实施例

<List<T>>

(请注意,这不是按字母顺序排列的,蓝莓来自狒狒之前)

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

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' }

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

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

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

7 个答案:

答案 0 :(得分:8)

您需要SelectMany

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

修改

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

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

编辑2

输出: Apple,Anteater,Albatross,Blueberry,Baboon,Blackbird,Cranberry,Camel,Chicken,Dodo

答案 1 :(得分:1)

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

即可

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

答案 2 :(得分:1)

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

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

  

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

然后

  

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

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

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

static class Extensions
{
    public static IEnumerable<T> Merge<T>(this IEnumerable<IEnumerable<T>> source)
    {
        var queue = new Queue<IEnumerator<T>>();
        IEnumerator<T> 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();
        }
    }
}

请注意,我可以通过消除重复的部分并合并两个通道来轻松缩短它,但这会使其更低的可读性而不是更快(事实上,由于需要一些额外的检查,它会稍微慢一些)。

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

答案 3 :(得分:0)

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

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

答案 4 :(得分:0)

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

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

答案 5 :(得分:0)

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

public static class MyLinqExtensions
{
    public static void MySelect<T>(this IEnumerable<IEnumerable<T>> superlist)
    {
        int index = 0;

        foreach (IEnumerable<T> list in superlist)
        {
            if (index < list.Count())
            {
                yield return list.ElementAt(index);
            }

            index++;
        }
    }
}

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

答案 6 :(得分:0)

我看到你要做的事情:

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