我正在尝试在LINQ中执行复杂的GroupBy,但是我的键选择器出现问题。在下面的代码中,我能够通过我的密钥在一个方向上进行分组(通过SellerID,BuyerID),但我实际上还需要在反向中按我的密钥进行分组(通过SellerID,BuyerID或BuyerID,SellerID)。我对此查询的最终目标是,当键被反转时,我需要使资产金额为负数。这将允许我扣除双方存在的任何金额,然后我将最终只有那个特定方面有金额的记录。
以下代码应该解释它:
public class Record
{
public int RecordID;
public int SellerID;
public int BuyerID;
public List<Asset> Assets;
}
public class Asset
{
public int AssetID;
public decimal Amount;
}
var groups = new List<Record>
{
new Record { RecordID = 1, SellerID = 100, BuyerID = 200, Assets = new List<Asset> { new Asset { AssetID = 5, Amount = 10 }}},
new Record { RecordID = 2, SellerID = 100, BuyerID = 200, Assets = new List<Asset> { new Asset { AssetID = 5, Amount = 20 }}},
new Record { RecordID = 3, SellerID = 100, BuyerID = 200, Assets = new List<Asset> { new Asset { AssetID = 6, Amount = 60 }}},
new Record { RecordID = 4, SellerID = 200, BuyerID = 100, Assets = new List<Asset> { new Asset { AssetID = 5, Amount = 40 }}},
new Record { RecordID = 5, SellerID = 200, BuyerID = 100, Assets = new List<Asset> { new Asset { AssetID = 5, Amount = 50 }}},
new Record { RecordID = 6, SellerID = 200, BuyerID = 100, Assets = new List<Asset> { new Asset { AssetID = 6, Amount = 35 }}}
};
var result = groups.GroupBy(
r => new { r.SellerID, r.BuyerID },
r => r.Assets,
(r, assets) => new
{
r.SellerID,
r.BuyerID,
AssetSummation = assets.SelectMany(asset => asset).GroupBy(a => a.AssetID).Select(a2 => new { AssetID = a2.Key, Amount = a2.Sum(a3 => a3.Amount) })
});
我希望输出的内容如下:
我认为我有一个良好的开端,但我不知道从哪里开始。如何翻转键然后使金额为负数,这样我可以总结一下?我认为在我能够做到这一点后,我可以过滤出值为0的任何资产行(意味着记录是通过反向完成的。
编辑#1:也许我想要做的就是将groups变量加入到自身中以汇总连接两侧的所有匹配记录。因此,我最终会将左侧的SellerID加入右侧的BuyerID,将左侧的BuyerID加入右侧的SellerID。
答案 0 :(得分:1)
以下是返回预期结果的查询:
var result = records
.SelectMany(r => new[] { r, new Record { // 1
SellerID = r.BuyerID,
BuyerID = r.SellerID,
Assets = r.Assets.Select(a => new Asset {
AssetID = a.AssetID,
Amount = -a.Amount
}).ToList() }})
.GroupBy(r => new { r.SellerID, r.BuyerID }) // 2
.Select(g => new { // 3
Seller = g.Key.SellerID,
Buyer = g.Key.BuyerID,
Assets = g.SelectMany(r => r.Assets)
.GroupBy(a => a.AssetID)
.Select(ag => new {
AssetID = ag.Key,
Amount = ag.Sum(a => a.Amount) })
.Where(x => x.Amount > 0) });
它是如何工作的?很简单:
SelectMany
展平所有记录。 BTW 您可以在最后一个选择语句中创建Record
和Asset
个对象,而不是返回匿名对象。
答案 1 :(得分:0)
助手类:
public class RecordItem
{
public int SellerID;
public int BuyerID;
public int AssetID;
public decimal Amount;
}
平等比较器:
public class RecordItemEqualityComparer : IEqualityComparer<RecordItem>
{
public bool Equals(RecordItem x, RecordItem y)
{
if (x.AssetID != y.AssetID)
return false;
if (x.BuyerID == y.BuyerID && x.SellerID == y.SellerID)
return true;
if (x.BuyerID == y.SellerID && x.SellerID == y.BuyerID)
return true;
return false;
}
public int GetHashCode(RecordItem obj)
{
return string.Format("{0}_{1}", obj.BuyerID * obj.SellerID, obj.AssetID).GetHashCode();
}
}
和LINQ查询:
var recordItemComparer = new RecordItemEqualityComparer();
var items = groups.SelectMany(r => r.Assets.Select(a => new RecordItem {
BuyerID = r.BuyerID,
SellerID = r.SellerID,
AssetID =a.AssetID,
Amount = a.Amount
}))
.GroupBy(ri => ri, recordItemComparer)
.Select(g => new RecordItem() {
BuyerID = g.Key.BuyerID,
SellerID = g.Key.SellerID,
AssetID = g.Key.AssetID,
Amount = g.Sum(ri => (ri.BuyerID == g.Key.BuyerID) ? ri.Amount : -1 * ri.Amount)
}).ToList();
返回您想要的内容:包含2个项目的列表。总和是正确计算的,但买方和卖方的顺序可能不正确,因此总和可能是-60
代替60
。
PS。这是一个非常好的挑战!