如何展平嵌套对象 (LINQ)
How to flatten nested objects (LINQ)
我正在旧的 Winforms 网格上做一些工作,我有两个模型,我试图展平并分配给 DataGridView。
这是我的示例模型。
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; }
}
数据的工作方式是必须首先创建一个仓库,然后将其分配给每个 StockItem
。一个StockItem
可能拥有所有的仓库,也可能只有一个。
我需要展平数据,以便网格显示 StockName
,然后是库存项目的所有关联仓库。
例子
StockCode1 Warehouse1 Warehouse2 Warehouse3
StockCode2 Warehouse1 Warehouse2
StockCode2 Warehouse1 Warehouse3
我试图通过 Linq 查询来执行此操作,但只能根据 StockItem\Warehouse.
获取一条记录
我不推荐它,但您可以使用dynamic对象来创建具有您想要的形状的对象。 这样做不是常见的 C# 模式。 这在 Python 或 Javascript 等语言中更为常见。
C# 是一种强类型语言,冒险进入动态对象的世界应该 只有 在绝对必要时才考虑(想想解析一个 json blob)。我强烈认为你重新评估了你需要做的事情并从不同的角度处理它。
这应该能满足您的需求:
var flattened = stockItems
.Select(x => new {
StockName = x.StockName,
WarehouseNames = x.Warehouses
.Select(y => y.WarehouseName)
.ToList() })
.ToList();
它将生成包含 StockName
和 WarehouseName
字符串列表的项目集合。 ToList
添加以枚举查询。
对于这些示例数据:
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" }
}
}
};
我得到以下结果:
像这样:
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);
}
您可以通过创建一个您可以轻松用作 gridview 源的 DataTable
来实现它。首先添加所有列,然后为每个库存添加仓库:
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);
}
我正在旧的 Winforms 网格上做一些工作,我有两个模型,我试图展平并分配给 DataGridView。
这是我的示例模型。
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; }
}
数据的工作方式是必须首先创建一个仓库,然后将其分配给每个 StockItem
。一个StockItem
可能拥有所有的仓库,也可能只有一个。
我需要展平数据,以便网格显示 StockName
,然后是库存项目的所有关联仓库。
例子
StockCode1 Warehouse1 Warehouse2 Warehouse3
StockCode2 Warehouse1 Warehouse2
StockCode2 Warehouse1 Warehouse3
我试图通过 Linq 查询来执行此操作,但只能根据 StockItem\Warehouse.
获取一条记录我不推荐它,但您可以使用dynamic对象来创建具有您想要的形状的对象。 这样做不是常见的 C# 模式。 这在 Python 或 Javascript 等语言中更为常见。
C# 是一种强类型语言,冒险进入动态对象的世界应该 只有 在绝对必要时才考虑(想想解析一个 json blob)。我强烈认为你重新评估了你需要做的事情并从不同的角度处理它。
这应该能满足您的需求:
var flattened = stockItems
.Select(x => new {
StockName = x.StockName,
WarehouseNames = x.Warehouses
.Select(y => y.WarehouseName)
.ToList() })
.ToList();
它将生成包含 StockName
和 WarehouseName
字符串列表的项目集合。 ToList
添加以枚举查询。
对于这些示例数据:
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" }
}
}
};
我得到以下结果:
像这样:
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);
}
您可以通过创建一个您可以轻松用作 gridview 源的 DataTable
来实现它。首先添加所有列,然后为每个库存添加仓库:
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);
}