[英]How to group object in C# by its equality using Linq
场景:
我有一个对象“ Order
”的列表,并希望按相同的List<OrderLine>
属性分组,相同的意思是相同数量的行以及相同订单中相同的Sku / Quantity值”并返回订单列表值分组:
class Order
{
public int OrderNumber { get; set; }
public List<OrderLine> Lines { get; set; }
}
class OrderLine
{
public string Sku { get; set; }
public int Quantity { get; set; }
}
输入样本:
+-------------+-----+----------+
| OrderNumber | Sku | Quantity |
+-------------+-----+----------+
| 1 | A | 10 |
| 1 | B | 20 |
| 2 | A | 10 |
| 3 | A | 10 |
| 3 | B | 20 |
+-------------+-----+----------+
所需的输出:
Lines = Lines.Count();
每个分组相同的行数
Pieces = SUM(OrderLine.Quantity);
每个相同订单的所有数量之和。
+-----------------+-------+--------+
| TotalIdenticals | Lines | Pieces |
+-----------------+-------+--------+
| 1 | 1 | 10 |
| 2 | 2 | 30 |
+-----------------+-------+--------+
我使用表表示法使其更加清晰。 因此,如上所述,只有1条记录包含1行(第2阶)和数量10。另一方面,有两个记录具有相同的行列表(第1和3阶)
所以我需要在运行linq算法后,它将为我生成一种
> "identical 1".Orders -> [2]
> "identical 2".Order -> [1,3]
我想做什么?
var identicals = orderList.GroupBy(x => x.Lines)
.Where(g => g.Count() > 1)
.Select(g => g.Key)
.ToList();
上面的代码不起作用,基本上我只需要能够将Lines属性分组(这样就等于其他OrderLines),那么我将能够生成行/片段的输出...现在唯一的问题是能够通过行列表对象相似性对我的对象顺序列表进行分组。
我希望我的问题很清楚,如果您需要更多详细信息,请告诉我,我将在这里添加。
第一步-为了在Order
和OrderItem
类上使用GroupBy()
,必须实现Equals()
和GetHashCode()
或为这两个类创建EqualityComparer
。
按Order
覆盖Equals()
和GetHashCode()
(仅基于Lines
属性):
public class Order
{
public int OrderNumber { get; set; }
public List<OrderLine> Lines { get; set; }
protected bool Equals(Order other)
{
var equals = OrderLinesEquals(Lines, other.Lines);
return equals;
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((Order) obj);
}
public override int GetHashCode()
{
if (Lines == null)
{
return 0;
}
unchecked
{
int hash = 19;
foreach (OrderLine item in Lines.OrderBy(x => x.Sku, StringComparer.OrdinalIgnoreCase))
{
hash = hash * 31 + item.GetHashCode();
}
return hash;
}
}
private bool OrderLinesEquals(List<OrderLine> x, List<OrderLine> y)
{
if (ReferenceEquals(x, y))
{
return true;
}
if (x == null || y == null)
{
return false;
}
bool areEquivalent = x.Count == y.Count && !x.Except(y).Any();
return areEquivalent;
}
public override string ToString()
{
return $"Sku: {Sku ?? "[null]"}, Quantity: {Quantity}";
}
}
在OrderItem
重写Equals()
和GetHashCode()
(基于Sku
和Quantity
属性):
public class OrderLine
{
public string Sku { get; set; }
public int Quantity { get; set; }
protected bool Equals(OrderLine other)
{
return string.Equals(Sku, other.Sku) && Quantity == other.Quantity;
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((OrderLine) obj);
}
public override int GetHashCode()
{
unchecked
{
return ((Sku != null ? Sku.GetHashCode() : 0) * 397) ^ Quantity;
}
}
}
测试代码-订单清单:
var order1 = new Order
{
OrderNumber = 1,
Lines = new List<OrderLine>
{
new OrderLine
{
Quantity = 10,
Sku = "A"
},
new OrderLine
{
Quantity = 20,
Sku = "B"
}
}
};
var order2 = new Order
{
OrderNumber = 2,
Lines = new List<OrderLine>
{
new OrderLine
{
Quantity = 10,
Sku = "A"
}
}
};
var order3 = new Order
{
OrderNumber = 3,
Lines = new List<OrderLine>
{
new OrderLine
{
Quantity = 20,
Sku = "B"
},
new OrderLine
{
Quantity = 10,
Sku = "A"
}
}
};
var order4 = new Order
{
OrderNumber = 4,
Lines = new List<OrderLine>
{
new OrderLine
{
Quantity = 20,
Sku = "B"
},
new OrderLine
{
Quantity = 10,
Sku = "A"
}
}
};
var order5 = new Order
{
OrderNumber = 5,
Lines = new List<OrderLine>
{
new OrderLine
{
Quantity = 30,
Sku = "C"
}
}
};
var order6 = new Order
{
OrderNumber = 6,
Lines = new List<OrderLine>
{
new OrderLine
{
Quantity = 40,
Sku = "C"
}
}
};
var order7 = new Order
{
OrderNumber = 7,
Lines = new List<OrderLine>
{
new OrderLine
{
Quantity = 30,
Sku = "C"
}
}
};
var orderList = new List<Order>(new[] {order1, order2, order3, order4, order5, order6, order7});
分组订单:
var identicalOrders = orderList.GroupBy(x => x)
.Where(g => g.Count() > 1)
.Select(g => new
{
Count = g.Count(),
OrderItems = g.Key.Lines,
OrderNumbers = orderList.Where(x => x.Equals(g.Key))
.Select(x => x.OrderNumber)
.ToList()
})
.ToList();
输出:
为了能够按Lines
分组,您需要实现IEqualityComparer<List<OrderLine>>
并将其传递给GroupBy
方法: var groups = orders.GroupBy(o => o.Lines, o => o, new OrderLineEqualityComparer());
internal class OrderLineEqualityComparer : IEqualityComparer<List<OrderLine>>
{
public bool Equals(List<OrderLine> x, List<OrderLine> y)
{
throw new NotImplementedException();
}
public int GetHashCode(List<OrderLine> obj)
{
throw new NotImplementedException();
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.