繁体   English   中英

如何进行LINQ GroupBy可以反转键的位置?

[英]How do I do a LINQ GroupBy where the key can be reversed?

我想在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) })
    });

我希望我的输出是以下内容:

  • 记录1
    • 卖方:100
    • 买方:200
    • 资产:
      • 财富
        • 资产ID:6
        • 金额:25
  • 记录2
    • 卖方:200
    • 买方:100
    • 资产:
      • 资产ID:5
      • 金额:60

我想我有一个好的开始,但我不知道从哪里开始。 如何翻转键然后使金额为负数,这样我可以总结一下? 我认为在我能够做到这一点后,我可以过滤出值为0的任何资产行(意味着记录是通过反向完成的)。

编辑#1:也许我想要做的就是将groups变量加入到自身中以汇总连接两侧的所有匹配记录。 因此,我最终会将左侧的SellerID加入右侧的BuyerID,将左侧的BuyerID加入右侧的SellerID。

这是查询,它返回您的预期结果:

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) });

这个怎么运作? 非常简单:

  1. 对于每条记录,我选择两条记录 - 一条是原样,另一条是反向卖方和买方(也是所有资产都有负数)。 然后我用SelectMany压平所有记录。
  2. 并按卖家和买家分组。
  3. 休息是从每个组中简单计算资产数量。

BTW而不是返回匿名对象,您可以在最后一个select语句中创建RecordAsset对象。

助手班:

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。 这是一个非常好的挑战!

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM