C#使用LINQ将带有嵌套列表的两个列表合并为一个

时间:2016-03-25 11:46:03

标签: c# linq

我在c#中有两个以下结构列表。 &lt;&gt;中的每个名称是一个清单。我想将这两个列表合并为一个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);
            }
        }

1 个答案:

答案 0 :(得分:0)

您可能一直在寻找.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名称形成具有相同名称的地图组。 Select获取每个组并使用该组中所有Map的内容创建一个新的Maps(这是合并!)。然后我对新的合并地图的内容做了同样的事情,并将MapProperty组合起来并合并/合并它们。

在您的具体情况下,您需要更多的工作和级别来合并所有内容。我建议找一种方法为树中的每个级别制作Merge方法,并从每个父级别调用下一级别,这样您就不会以大规模 linq chain。