2013-03-18 35 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。這是一個非常好的挑戰!