关于c#:如何展平嵌套对象(LINQ)

How to flatten nested objects (LINQ)

我正在一个旧的winforms网格上做一些工作,我有两个模型,我正试图扁平化并分配给一个DataGridView。

这是我的样品模型。

1
2
3
4
5
6
7
8
9
10
11
12
public class StockItem
{
     public string StockName { get; set; }
     public int Id { get; set; }
     public List<Warehouse> Warehouses { get; set; }
}

public class Warehouse
{
     public string WarehouseName { get; set; }
     public int Id { get; set; }
}

数据的工作方式是,必须首先创建一个仓库,然后将其分配给每个StockItemStockItem可以拥有所有仓库,也可以只有一个仓库。

我需要扁平化数据,以便网格显示StockName,然后显示库存项的所有关联仓库。

例子

1
2
3
StockCode1      Warehouse1   Warehouse2   Warehouse3
StockCode2      Warehouse1   Warehouse2  
StockCode2      Warehouse1                Warehouse3

我尝试通过LINQ查询来完成此操作,但只能为每个stockitemwarehouse获取一条记录。


您可以通过创建一个DataTable来实现它,您可以轻松地将它用作GridView的源代码。首先添加所有列,然后为每个库存添加仓库:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
var warehouseNames =
    stocks
    .SelectMany(x => x.Warehouses.Select(y => y.WarehouseName)).Distinct();

var dt = new DataTable();
dt.Columns.Add("StockCode");

foreach (var name in warehouseNames)
{
    dt.Columns.Add(name);
}

foreach (var stock in stocks)
{
    var row = dt.NewRow();
    row["StockCode"] = stock.Id;
    foreach (var warehouse in stock.Warehouses)
    {
        row[warehouse.WarehouseName] = warehouse.Id;
    }
    dt.Rows.Add(row);
}

Warehouses


像这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
var availableWarehouses = new [] {
    new Warehouse {
        WarehouseName ="Warehouse1",
        Id = 1
    },
    new Warehouse {
        WarehouseName ="Warehouse2",
        Id = 2
    },
    new Warehouse {
        WarehouseName ="Warehouse3",
        Id = 3
    }
};

var stocks = new [] {
    new StockItem {
        StockName ="StockCode1",
        Id = 1,
        Warehouses = new List<Warehouse> { availableWarehouses[0], availableWarehouses[1], availableWarehouses[2] }
    },
    new StockItem {
        StockName ="StockCode2",
        Id = 2,
        Warehouses = new List<Warehouse> { availableWarehouses[0], availableWarehouses[1] }
    },
    new StockItem {
        StockName ="StockCode3",
        Id = 3,
        Warehouses = new List<Warehouse> { availableWarehouses[0], availableWarehouses[2] }
    }
};

var flatten = stocks.Select(item => new {
        StockName = item.StockName,
        WarehousesNames = availableWarehouses.Select(warehouse => item.Warehouses.Contains(warehouse) ? warehouse.WarehouseName :"         ")
            .Aggregate((current, next) => current +"\t" + next)
    });

foreach(var item in flatten) {
    Console.WriteLine("{0}\t{1}", item.StockName, item.WarehousesNames);
}


我不建议这样做,但您可以使用动态对象创建具有所需形状的对象。这样做不是一种常见的C模式。这在诸如python或javascript之类的语言中更常见。

C是一种强类型语言,只有在绝对必要的时候才应该考虑进入动态对象的世界(考虑解析JSON blob)。我强烈认为你需要重新评估你需要做什么,并从另一个角度来对待它。


这应该给你你所需要的:

1
2
3
4
5
6
7
var flattened = stockItems
    .Select(x => new {
                 StockName = x.StockName,
                 WarehouseNames = x.Warehouses
                                  .Select(y => y.WarehouseName)
                                  .ToList() })
    .ToList();

它将导致包含StockNameWarehouseName字符串列表的项集合。添加ToList以枚举查询。

对于这些示例数据:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
List<StockItem> stockItems = new List<StockItem>
{
    new StockItem
    {
        StockName ="A",
        Id = 1,
        Warehouses = new List<Warehouse>
        {
            new Warehouse { Id = 1, WarehouseName ="x" },
            new Warehouse { Id = 2, WarehouseName ="y" }
        }
    },
    new StockItem
    {
        StockName ="B",
        Id = 2,
        Warehouses = new List<Warehouse>
        {
            new Warehouse { Id = 3, WarehouseName ="z" },
            new Warehouse { Id = 4, WarehouseName ="w" }
        }
    }
};

我得到了以下结果:

enter image description here