【问题标题】:C# Combine two lists with nested lists into one using LINQC# 使用 LINQ 将两个带有嵌套列表的列表合二为一
【发布时间】:2016-03-25 11:46:02
【问题描述】:

我在 c# 中有两个以下结构的列表。 中的每个名称都是一个列表。我想将这两个列表合并为一个List<Server>。以下伪代码显示了两个这样的列表以及结果的样子:

<Servers>                           <Servers>
+...                                +...
+-- Server A,1                      +-- Server A,1
|       +...                        |       +...    
|       +--<Maps>                   |       +--<Maps>
|           +--Map x                |           +--Map x
|               +...                |               +...    
|               +--<Times>          |               +--<Times>
|                   +--Time i       |                   +--Time l
|                   +--Time j       |               +--<Jumps>
|               +--<Jumps>          |                   +--Jump t
|                   +--Jump s       |                   +--Jump u
|           +--Map y                |           +--Map z
|               +...                |               +...
|               +--<Times>          |               +--<Jumps>  
|                   +-- Time k      |                   +--Jump v
+-- Server B,1                      +-- Server B,2

结果应该是:

<Servers>
+...
+-- Server A,1
|   +...
|   +--<Maps>
|       +-- Map x
|           +...
|           +--<Times>
|               +--Time i
|               +--Time j
|               +--Time l
|           +--<Jumps>
|               +--Jump s
|               +--Jump t
|               +--Jump u
|       +-- Map y
|           +...
|           +--<Times>
|               +--Time k
|       +-- Map z
|           +...
|           +--<Jumps>
|               +--Jump v
+-- Server B,1
+-- Server B,2

我尝试对 linq 使用完全外连接,但结果也不是我想要的,原因是我不明白具有相同密钥的服务器不匹配,所以我总是有相同服务器的副本但不同数据。那时我不再尝试使用 linq 来做这件事,而是使用循环手动合并列表。

下面的代码给了我想要的结果列表。现在我将使用它,直到找到更好的方法。有没有一种简单/更短的方法可以用 linq 做到这一点?使用 lambda 表达式?

  foreach (Server importServer in oImportList)
        {
            if (!CoreList.Contains(importServer))
            {
                CoreList.Add(importServer);
                continue;
            }

            Server coreServer = CoreList.FirstOrDefault(o => o.Equals(importServer));
            coreServer.Name = importServer.Name;
            coreServer.KZTimerVersion = importServer.KZTimerVersion;
            foreach(Map importMap in importServer.Maps)
            {
                if (!coreServer.Maps.Contains(importMap))
                {
                    coreServer.Maps.Add(importMap);
                    continue;
                }
                Map coreMap = coreServer.Maps.FirstOrDefault(o => o.Equals(importMap));
                coreMap.Jumps.Concat(importMap.Jumps);
                coreMap.Times.Concat(importMap.Times);
            }
        }

【问题讨论】:

  • 我认为你的想法是对的。看起来您只是将嵌套的Joins 传递了错误的参数!例如,在Maps 加入中,您尝试加入coreServer.MapsoImportList... 我怀疑这应该是coreServer.MapsimportServer.Maps
  • 我能够在您的帮助下获得代码,但后来我发现结果不是我想要的列表。它只包含两个列表中都存在的元素。
  • 啊,抱歉...我很少使用Join,所以我把它弄混了。我使用GroupBySelect 取消删除了可能对您有用的答案。我之前删除了它,因为我认为我走错了路。

标签: c# linq


【解决方案1】:

您可能一直在寻找.Union,但这不涉及嵌套列表,所以我认为这不是您真正需要的。我设置了一个完整的例子——虽然结构和你的不一样我认为你可以很容易地应用核心 linq:

using System;
using System.Collections.Generic;
using System.Linq;

public class Map
{
    public string Name { get; set; }
    public IEnumerable<MapProperty> Properties { get; set; }
}

public class MapProperty
{
    public string Name { get; set; }
}

public class Test
{
    public static IEnumerable<Map> Merge(IEnumerable<Map> a, IEnumerable<Map> b)
    {
        return a.Concat(b)
            .GroupBy(map => map.Name)
            .Select(mg => new Map
            {
                Name = mg.Key,
                Properties = mg.SelectMany(v => v.Properties)
                    .GroupBy(p => p.Name)
                    .Select(pg => new MapProperty { Name = pg.Key })
            });
    }

    public static void Main()
    {
        var mapsA = new[]
        {
            new Map
            {
                Name = "MapA",
                Properties = new[]
                {
                    new MapProperty { Name = "Jumps" },
                    new MapProperty { Name = "Hops" },
                }
            },
            new Map
            {
                Name = "MapB",
                Properties = new[]
                {
                    new MapProperty { Name = "Jumps" },
                    new MapProperty { Name = "Skips" },
                }
            }
        };
        var mapsB = new[]
        {
            new Map
            {
                Name = "MapA",
                Properties = new[]
                {
                    new MapProperty { Name = "Jumps" },
                    new MapProperty { Name = "Times" },
                }
            },
            new Map
            {
                Name = "MapC",
                Properties = new[]
                {
                    new MapProperty { Name = "Jumps" },
                    new MapProperty { Name = "Skips" },
                }
            }
        };

        var mapsC = Merge(mapsA, mapsB);

        foreach (var map in mapsC)
        {
            Console.WriteLine($"{map.Name}");
            foreach (var prop in map.Properties)
            {
                Console.WriteLine($"\t{prop.Name}");
            }
        }
    }
}

输出:

MapA
    Jumps
    Hops
    Times
MapB
    Jumps
    Skips
MapC
    Jumps

Merge 是重要的一点。它首先将ab 放在一个列表中,然后GroupBy name 形成具有相同名称的映射组。 Select 获取每个组并使用该组中所有Maps 的内容创建一个新的Map(这是合并!)。然后我对新合并地图的内容做了同样的事情,并将MapPropertys 分组并合并/合并它们。

在您的具体情况下,您将需要更多的工作和关卡来合并所有内容。我建议找到一种方法为树中的每个级别创建一个 Merge 方法,并从每个父级别调用下一个级别,这样你就不会得到一个 ma​​ssive linq 链。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-05-10
    • 2021-11-29
    • 2017-10-01
    • 2016-08-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多