C# 合并多个列表

C# 合并多个列表,c#,.net,linq,list,C#,.net,Linq,List,假设我有一个列表列表,list,其中所有列表都包含0个或多个项目,很可能,但不一定都是相同的数字。我想要一份包含所有项目的清单。。。但我希望顺序如下:首先是所有列表中的第一项,以便它们出现在“超级列表”中 前 (注意这不是字母顺序,蓝莓在狒狒之前) 当然,只要列表不是空的,我可以使用计数器循环“superlist”,将项目逐个添加到resultslist: int i = 0; bool done = false; while (!done) { bool found = false;

假设我有一个列表列表,
list
,其中所有列表都包含0个或多个项目,很可能,但不一定都是相同的数字。我想要一份包含所有项目的清单。。。但我希望顺序如下:首先是所有列表中的第一项,以便它们出现在“超级列表”中

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

当然,只要列表不是空的,我可以使用计数器循环“superlist”,将项目逐个添加到resultslist:

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函数来实现这一点会更好。我一直在调查,但没能让他们工作

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


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

您需要
选择多个

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


输出:
苹果、食蚁兽、信天翁、蓝莓、狒狒、黑鸟、蔓越莓、骆驼、鸡、渡渡鸟
只需使用
MyListOfLists。选择多个(x=>x)。按降序排列(x=>x)。ToList()

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

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<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++;
        }
    }
}
公共静态类MyLinqExtensions
{
公共静态void MySelect(此IEnumerable超级列表)
{
int指数=0;
foreach(超级列表中的IEnumerable列表)
{
if(索引

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

我知道您要做什么:

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);

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

在这个问题上有两种相互矛盾的说法:

但使用一些优化的LINQ函数来实现这一点会更好

然后

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

优化的漂亮的之间有很大的区别。当然,这取决于“优化”的含义。漂亮、可读、较短的代码可能被认为是针对维护而优化的,但大多数情况下不是针对性能。因此,让我们谈谈性能。被接受的答案可能看起来很酷,但在时间和空间上都是低效的(由于group by),甚至没有提供类似“标准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();
        }
    }
}
静态类扩展
{
公共静态IEnumerable合并(此IEnumerable源)
{
var queue=新队列();
IEnumerator itemEnumerator=null;
尝试
{
//第一次通过:产生第一个元素(如有)并安排下一个元素(如有)
foreach(源中的变量列表)
{
itemEnumerator=list.GetEnumerator();
如果(!itemEnumerator.MoveNext())
itemEnumerator.Dispose();
其他的
{
产生返回itemEnumerator.Current;
if(itemEnumerator.MoveNext())
排队(itemEnumerator);
其他的
itemEnumerator.Dispose();
}
}
//第二步:生成当前元素并计划下一步(如果有)
而(queue.Count>0)
{
itemEnumerator=queue.Dequeue();
产生返回itemEnumerator.Current;
if(itemEnumerator.MoveNext())
排队(itemEnumerator);
其他的
itemEnumerator.Dispose();
}
}
最后
{
如果(itemEnumerator!=null)itemEnumerator.Dispose();
而(queue.Count>0)queue.Dequeue().Dispose();
}
}
}
请注意,我可以通过消除重复部分并合并两个过程轻松地缩短它,但这会降低可读性,而不会加快速度(事实上是这样的)
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();
        }
    }
}