如何在 LINQ 中对匿名对象列表进行分组和 merge/flatten
How to group and merge/flatten a list of anonymous objects in LINQ
我有一个 LINQ 查询生成的匿名对象列表,我无权修改。
对象具有以下属性:
OrderId, RepId, FirstName, LastName, Address
每个 "Rep" 经常下多个订单,所以有很多行,唯一的区别是 OrderId。有一个要求,如果同一个 Rep 下了多个订单,将这些订单以 6 个为一组,以新的结构进行批处理:
OrderId1, OrderId2, ..., OrderId6, RepId, FirstName, LastName, Address
但是如果销售代表下了 8 个订单,那么会有一批 6 个和一批 2 个。所以新对象并不总是具有相同数量的属性。
我已经开始按 RepId 对初始结果集进行分组,但我不知道下一步该怎么做。
这可以使用 LINQ 吗?
第一个选项。
如果你想得到6个OrderId-s作为一个列表,你可以创建
class OrderBundle
{
public int RepId { get; set; }
public List<int> OrderIds { get; set; }
}
将您的项目分组:
var orderBundels = orderList
.GroupBy(m => m.RepId)
.Select(g => new OrderBundle
{
RepId = g.Key,
OrderIds = g.Select(m => m.OrderId).ToList()
});
然后将他们分组:
List<OrderBundle> dividedOrderBundels = new List<OrderBundle>();
foreach (OrderBundle orderBundle in orderBundels)
{
int bundelCount = (int)Math.Ceiling(orderBundle.OrderIds.Count() / 6.0);
for (int i = 0; i < bundelCount; i++)
{
OrderBundle divided = new OrderBundle
{
RepId = orderBundle.RepId,
OrderIds = orderBundle.OrderIds.Skip(i * 6).Take(6).ToList()
};
dividedOrderBundels.Add(divided);
}
}
第二个选项:
您可以在不创建如下模型的情况下获得相同的结果:
var result = orderList
.GroupBy(m => m.RepId)
.SelectMany(g => g.Select((m, i) => new
{
RepId = g.Key,
FirstName = m.FirstName,
LastName = m.LastName,
Address = m.Address,
OrderId = m.OrderId,
BunleIndex = i / 6
}))
.GroupBy(m => m.BunleIndex)
.Select(g => new
{
RepId = g.Select(m => m.RepId).First(),
FirstName = g.Select(m => m.FirstName).First(),
LastName = g.Select(m => m.LastName).First(),
Address = g.Select(m => m.Address).First(),
OrderIds = g.Select(m => m.OrderId).ToList()
})
.ToList()
由于您的输出具有具有不同架构的匿名对象,这使事情变得更加复杂。
理想情况下,您应该将实体 class 设计为使用订单列表而不是像 "OrderId1"、"OrderId2" 那样的 属性... 这不可扩展且容易出错.但是对于那个特定的问题,我们可以结合 LINQ 和 ExpandoObject 来实现。
orders.GroupBy(order => order.RepId)
.SelectMany(orderGroup => orderGroup.Select((order, i) => new {
Order = order,
ReqId = orderGroup.Key,
SubGroupId = i / 6
}))
.GroupBy(h => new {
ReqId = h.ReqId,
SubGroupId = h.SubGroupId,
FirstName = h.Order.FirstName,
LastName = h.Order.LastName,
Address = h.Order.Address
})
.Select(orderWithRichInfo => {
dynamic dynamicObject = new ExpandoObject();
int i = 1;
foreach(var o in orderWithRichInfo)
{
((IDictionary<string, object>)dynamicObject).Add("OrderId" + i, o.Order.OrderId);
i++;
}
((IDictionary<string, object>)dynamicObject).Add("FirstName", orderWithRichInfo.Key.FirstName);
((IDictionary<string, object>)dynamicObject).Add("LastName", orderWithRichInfo.Key.LastName);
((IDictionary<string, object>)dynamicObject).Add("Address", orderWithRichInfo.Key.Address);
return dynamicObject;
});
希望对您有所帮助。
我有一个 LINQ 查询生成的匿名对象列表,我无权修改。
对象具有以下属性:
OrderId, RepId, FirstName, LastName, Address
每个 "Rep" 经常下多个订单,所以有很多行,唯一的区别是 OrderId。有一个要求,如果同一个 Rep 下了多个订单,将这些订单以 6 个为一组,以新的结构进行批处理:
OrderId1, OrderId2, ..., OrderId6, RepId, FirstName, LastName, Address
但是如果销售代表下了 8 个订单,那么会有一批 6 个和一批 2 个。所以新对象并不总是具有相同数量的属性。
我已经开始按 RepId 对初始结果集进行分组,但我不知道下一步该怎么做。
这可以使用 LINQ 吗?
第一个选项。
如果你想得到6个OrderId-s作为一个列表,你可以创建
class OrderBundle
{
public int RepId { get; set; }
public List<int> OrderIds { get; set; }
}
将您的项目分组:
var orderBundels = orderList
.GroupBy(m => m.RepId)
.Select(g => new OrderBundle
{
RepId = g.Key,
OrderIds = g.Select(m => m.OrderId).ToList()
});
然后将他们分组:
List<OrderBundle> dividedOrderBundels = new List<OrderBundle>();
foreach (OrderBundle orderBundle in orderBundels)
{
int bundelCount = (int)Math.Ceiling(orderBundle.OrderIds.Count() / 6.0);
for (int i = 0; i < bundelCount; i++)
{
OrderBundle divided = new OrderBundle
{
RepId = orderBundle.RepId,
OrderIds = orderBundle.OrderIds.Skip(i * 6).Take(6).ToList()
};
dividedOrderBundels.Add(divided);
}
}
第二个选项:
您可以在不创建如下模型的情况下获得相同的结果:
var result = orderList
.GroupBy(m => m.RepId)
.SelectMany(g => g.Select((m, i) => new
{
RepId = g.Key,
FirstName = m.FirstName,
LastName = m.LastName,
Address = m.Address,
OrderId = m.OrderId,
BunleIndex = i / 6
}))
.GroupBy(m => m.BunleIndex)
.Select(g => new
{
RepId = g.Select(m => m.RepId).First(),
FirstName = g.Select(m => m.FirstName).First(),
LastName = g.Select(m => m.LastName).First(),
Address = g.Select(m => m.Address).First(),
OrderIds = g.Select(m => m.OrderId).ToList()
})
.ToList()
由于您的输出具有具有不同架构的匿名对象,这使事情变得更加复杂。
理想情况下,您应该将实体 class 设计为使用订单列表而不是像 "OrderId1"、"OrderId2" 那样的 属性... 这不可扩展且容易出错.但是对于那个特定的问题,我们可以结合 LINQ 和 ExpandoObject 来实现。
orders.GroupBy(order => order.RepId)
.SelectMany(orderGroup => orderGroup.Select((order, i) => new {
Order = order,
ReqId = orderGroup.Key,
SubGroupId = i / 6
}))
.GroupBy(h => new {
ReqId = h.ReqId,
SubGroupId = h.SubGroupId,
FirstName = h.Order.FirstName,
LastName = h.Order.LastName,
Address = h.Order.Address
})
.Select(orderWithRichInfo => {
dynamic dynamicObject = new ExpandoObject();
int i = 1;
foreach(var o in orderWithRichInfo)
{
((IDictionary<string, object>)dynamicObject).Add("OrderId" + i, o.Order.OrderId);
i++;
}
((IDictionary<string, object>)dynamicObject).Add("FirstName", orderWithRichInfo.Key.FirstName);
((IDictionary<string, object>)dynamicObject).Add("LastName", orderWithRichInfo.Key.LastName);
((IDictionary<string, object>)dynamicObject).Add("Address", orderWithRichInfo.Key.Address);
return dynamicObject;
});
希望对您有所帮助。