3

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

前任。

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

(注意这不是字母顺序,蓝莓在狒狒之前)

当然,只要有一个不为空的列表,我就可以使用计数器循环遍历“超级列表”,将项目逐个添加到结果列表中:

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 函数来做到这一点会更好。我一直在研究ZipGroupByAggregate,但无法让它们工作。

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

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

4

7 回答 7

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

于 2013-08-23T18:47:57.930 回答
1

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

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

于 2013-08-23T18:48:29.167 回答
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();
        }
    }
}

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

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

于 2015-10-20T10:16:56.340 回答
0

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

var result = superlist.SelectMany(r=> r).ToList();
于 2013-08-23T18:48:30.680 回答
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);
于 2013-08-23T19:10:59.477 回答
0

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

var result = list.SelectMany(x=>x)
                 .OrderBy(x=>list.First(a=>a.Contains(x))
                                 .IndexOf(x));
于 2013-08-23T18:59:56.607 回答
0

如何从所有列表中选择第一个元素,然后是第二个元素,等等,没有简单或最佳的方法(通过性能和代码可读性)。但是,您可以从代码构建自己的扩展,或者如果您不喜欢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()结果以获取唯一值。

于 2013-08-23T19:00:57.690 回答