c# - Linq 按复杂类型的列表分组
问题描述
我正在尝试对列表的某些列表对象执行 Linq GroupBy。
public class ItemDto
{
public int ItemId { get; set; }
public string ProductName { get; set; }
public int Count { get; set; }
public List<CondimentDto> Condiments { get; set; }
}
public class CondimentDto
{
public CondimentDto(int ItemId, string productName, decimal originalPrice)
{
ItemId = ItemId;
ProductName = productName;
OriginalPrice = originalPrice;
}
public int PosItemId { get; set; }
public string ProductName { get; set; }
public decimal OriginalPrice { get; set; }
}
这是列表示例。
var items = new List<ItemDto>();
items.Add(new ItemDto(){ItemId = 1, ProductName = "Steak", Condiments = new List<CondimentDto>{new CondimentDto(11, "Mayonez", 1)}});
items.Add(new ItemDto() { ItemId = 1, ProductName = "Steak", Condiments = new List<CondimentDto> { new CondimentDto(11, "Mayonez", 1) } });
items.Add(new ItemDto() { ItemId = 1, ProductName = "Steak", Condiments = new List<CondimentDto> { new CondimentDto(11, "Hardal", 1) } });
这是我的 LinQ 查询;
var result = items.GroupBy(item => new {item.ItemId, item.Condiments})
.Select(x=>new ItemDto
{
ItemId = x.Key,
ProductName = x.First().ProductName,
Condiments = x.First().Condiments,
Count = x.Count()
})
.ToList();
我想要做的是我想按 ItemId 和 List of Condiments 对我的列表进行分组。我期望的结果是
{
items[
{
"ItemId": 1,
"ProductName": "Steak",
"Condiments": [
{
"ItemId": 11,
"Name": "Mayonez",
"Price": 1
}
],
"Count": 2
},
{
"ItemId": 1,
"ProductName": "Steak",
"Condiments": [
{
"ItemId": 11,
"Name": "Hardal",
"Price": 1
}
],
"Count": 1
}
]
}
结果,有两个相同的列表项,这就是为什么必须将其分组并且计数为 2 的原因。另一个不同的项目是调味品是 Hardal。它是独立的,计数为 1。
是否可以通过列表对象进行两组?顺便说一句,调味品可以是空的。然后它应该只按 ID 分组。
谢谢
解决方案
你可以在这里创建两个IEqualityComparer<T>
类。一个用于比较两个CondimentDto
对象是否相等,一个用于比较两个对象是否List<CondimentDto>
相等。我们通过CondimentComparer
toSequenceEqual()
来确定两个列表是否相等,我们通过ItemDtoComparer
to来对主列表中的对象GroupBy
进行分组。我们还必须创造我们自己的并确定平等。ItemDto
items
Equals()
GetHashCode()
public class CondimentComparer : IEqualityComparer<CondimentDto>
{
public bool Equals(CondimentDto x, CondimentDto y)
{
// If all the properties match, then its equal
return x.PosItemId == y.PosItemId &&
x.ProductName == y.ProductName &&
x.OriginalPrice == y.OriginalPrice;
}
// Get all hashcodes from object properties using XOR
// Might be better ways to do this
public int GetHashCode(CondimentDto obj)
{
return obj.PosItemId.GetHashCode() ^
obj.ProductName.GetHashCode() ^
obj.OriginalPrice.GetHashCode();
}
}
public class ItemDtoComparer : IEqualityComparer<ItemDto>
{
public bool Equals(ItemDto x, ItemDto y)
{
// If two condiments are null, then just compare ItemId
if (x.Condiments == null && y.Condiments == null)
{
return x.ItemId == y.ItemId;
}
// If one is null and the other is not, can't possiblly be equal
else if (x.Condiments == null && y.Condiments != null ||
x.Condiments != null && y.Condiments == null)
{
return false;
}
// If we get here we have two normal objects
// Compare two objects ItemId and lists of condiments
return x.ItemId == y.ItemId &&
x.Condiments.SequenceEqual(y.Condiments, new CondimentComparer());
}
public int GetHashCode(ItemDto obj)
{
// If condiments list is null, just get the ItemId hashcode
if (obj.Condiments == null)
{
return obj.ItemId.GetHashCode();
}
// Otherwise do the XOR of ItemId and the aggregated XOR of the condiment list properties
return obj.ItemId.GetHashCode() ^ obj.Condiments.Aggregate(0, (a, c) => a ^ c.PosItemId.GetHashCode() ^ c.ProductName.GetHashCode() ^ c.OriginalPrice.GetHashCode());
}
}
然后你可以传递ItemDtoComparer
给Groupby
:
var result = items
.GroupBy(item => item, new ItemDtoComparer()) // Pass ItemDtoComparer here
.Select(grp => new ItemDto
{
ItemId = grp.Key.ItemId,
ProductName = grp.Key.ProductName,
Condiments = grp.Key.Condiments,
Count = grp.Count()
})
.ToList();
dotnetfiddle.net上的演示
注意:以上GetHashCode()
实现是异或哈希码。这被认为不是最佳实践,并且遵循此答案中的建议可以提供更好的替代方案来避免哈希冲突。我只是选择了这种方法,因为它易于记忆和演示。
推荐阅读
- reactjs - React bootstrap Carousels 组件无法正常工作并出现错误
- react-native - 反应原生不会填满ios(移动)上的窗口
- java - HttpClientException:请求失败
- angular - 通过使用角度路由未选择默认路由
- c# - 以编程方式刷新 Windows 任务栏
- c# - Blogtrigger 在 Application Insights 中产生 HEAD 404 错误
- c# - 在 C# 中查找字符串尾随空格数的最佳方法是什么?
- javascript - 如何对javascript数组进行排序
- jquery - 数据表时刻插件未按预期工作
- python - 如何使用 Python 读取文件夹中的多个文本文件?