2013-03-18 67 views
4

我想在LINQ中做一个复杂的GroupBy,但我遇到了我的密钥选择器的麻烦。在下面的代码中,我可以通过我的密钥在一个方向(通过SellerID,BuyerID)进行分组,但实际上我需要通过我的密钥进行反向分组(通过SellerID,BuyerID或BuyerID,SellerID)。我的这个查询的最终目标是,当密钥倒转时,我需要使资产金额为负值。这将使我能够清除双方存在的任何金额,然后我只会得到具有特定方面金额的记录。我该如何做一个LINQ GroupBy钥匙可以颠倒的地方?

下面的代码应该解释一下:

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
    • 资产:
      • 资产
        • 由assetid:6
        • 额:25
  • 记录2
    • 卖家:200
    • 买方:100
    • 资产:
      • 由assetid:5
      • 金额:60

我想我有一个良好的开端,但我不知道在哪里何去何从。如何翻转键,然后将金额设为负值,以便我总结它们?我认为,在我能够做到这一点后,我可以过滤任何值为0的资产行(意味着记录是通过反向来实现的)。编辑#1:也许我想要做的是是加入组变量到自己,以汇总所有匹配记录在连接的两边,所以我最终将加入左边的SellerID到右边的BuyerID和左边的BuyerID到SellerID上右侧

回答

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

它是如何工作的?非常简单:

  1. 对于每条记录我选择两个记录 - 一个是原样,另一个是反向卖方和买方(也是所有资产都有负值)。然后我用SelectMany将所有记录弄平。
  2. 并由卖家和买家分组。
  3. 其余部分简单计算各组资产数量。

BTW,而不是返回匿名的对象,您可以创建在最后的SELECT语句您RecordAsset对象。

0

Helper类:

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