2012-04-27 30 views
3

我有一份現有持股的名單,我想將其列入另一份持股名單。我知道使用foreach & for循環是一個不好的方法,但我想不出使用LINQ修剪這種情況的好方法。有沒有更好的方法來做到這一點,也許用LINQ ish替換for/foreach循環?

private void CombineHoldings(List<Holding> holdingsToAdd, ref List<Holding> existingHoldings) 
{ 
    foreach (Holding holdingToAdd in holdingsToAdd) 
    { 
     Boolean found = false; 
     for (int i = 0; i < existingHoldings.Count; i++) 
     { 
      if (existingHoldings[i].Sector == holdingToAdd.Sector) 
      { 
       found = true; 
       existingHoldings[i].Percentage += holdingToAdd.Percentage; 
      } 
     } 
     if (!found) 
      existingHoldings.Add(holdingToAdd); 
    } 
    foreach (Holding holding in existingHoldings) 
     holding.Fund = "Combined Funds"; 
} 
+1

如果和for循環是路不好走,然後避開LINQ的,因爲LINQ的使用的foreach和在幕後循環。順便說一句 - 當'found = true'時,你應該在內部if循環中添加一個'break',否則,當你已經找到你的項目時,你將繼續迭代集合。 – 2012-04-27 23:52:26

+2

你爲什麼通過ref列表? – Robaticus 2012-04-27 23:52:55

+0

可能重複的[合併兩個IEnumerable s](http://stackoverflow.com/questions/590991/merging-two-ienumerablets) – Jason 2012-04-27 23:57:31

回答

0

如果您requently調用這個方法就行了,那麼我建議把它轉化爲列表類型的擴展方法,即

private static void CombineHoldings(this List<Holding> holdingsToAdd, ref List<Holding> existingHoldings) 
{ 
    foreach (Holding holdingToAdd in holdingsToAdd) 
    { 
     Boolean found = false; 
     for (int i = 0; i < existingHoldings.Count; i++) 
     { 
      if (existingHoldings[i].Sector == holdingToAdd.Sector) 
      { 
       found = true; 
       existingHoldings[i].Percentage += holdingToAdd.Percentage; 
      } 
     } 
     if (!found) 
      existingHoldings.Add(holdingToAdd); 
    } 
    foreach (Holding holding in existingHoldings) 
     holding.Fund = "Combined Funds"; 
} 

,這將讓你隨時隨地你已經創建了一個列表去

List<Holding> temp1 = new List<Holding>(); 
List<Holding> temp2 = new List<Holding>(); 
//add here to temp1 and temp2 
//then... 
temp1.CombineHoldings(temp2); 

使第一方法的靜態和把一個「本」的關鍵字中的第一個參數的前意味着它將擴展該類型

望着參數,雖然它可能會更有意義切換兩個,因此,它的添加到列表中調用像這樣的方法 -

private static void CombineHoldings(this List<Holding> existingHoldings, List<Holding> holdingsToAdd) 
0

我可能會去像這樣的東西:

private void CombineHoldings(List<Holding> holdingsToAdd, ref List<Holding> existingHoldings) 
{ 
    // group the new holdings by sector 
    var groupedHoldings = holdingsToAdd.GroupBy(h => h.Sector); 

    // now iterate over the groupings 
    foreach(var group in groupedHoldings) { 
     // calculate the sum of the percentages in the group 
     // we'll need this later 
     var sum = group.Sum(h => h.Percentage); 

     // get the index of a matching object in existing holdings 
     var existingHoldingIndex = existingHoldings.FindIndex(h => h.Sector == group.Key); 

     // yay! found one. add the sum of the group and our job's done. 
     if(existingHoldingIndex >= 0) { 
      existingHoldings[existingHoldingIndex].Percentage += sum; 
      continue; 
     } 

     // didn't find one, so take the first holding in the group, set its percentage to the sum 
     // and append that to the existing holdings table 
     var newHolding = group[0]; 
     newHolding.Percentage = sum; 

     existingHoldings.Add(newHolding); 
    } 
} 

表現方面,我不知道這是如何舉行。但它似乎更優雅。

0

你的問題有點含糊不清,你想擺脫foreach循環,因爲for loops are faster,因爲你覺得你有一個太多的循環,或者你想要更好的性能?

假設這是一個關於提高性能的問題,我建議將列表中的existingHoldings更改爲SortedList,其中T是Holding.Sector的類型。爲了獲得最佳性能,Sector應該像int一樣是integer variable type

private void CombineHoldings(List<Holding> holdingsToAdd, SortedList<int,Holding> existingHoldings) //Remove ref since List and SortedList are reference types and we are not changing the pointer. 
{ 

    for (int i = 0; i < holdingsToAdd.Count; i++) 
    { 
     if (existingHoldings.ContainsKey(holdingsToAdd[i].Sector)) 
     { 
      existingHoldings[holdingsToAdd[i].Sector].Percentage += holdingsToAdd[i].Percentage; 
     } 
     else 
     { 
      existingHoldings.Add(holdingsToAdd[i].Sector, holdingsToAdd[i]); 
     } 
    } 
    for (int i = 0; i < existingHoldings.Count; i++) 
    { 
     existingHoldings.Values[i].Fund = "Combined Funds"; 
    } 
} 

這種方法將導致O(M *日誌N + N),其中n是在existingHoldings和M元素的數量在holdingsToAdd元件的數量。不幸的是,所有現有的控制元素必須更新其基金價值,因爲它增加了該集合的額外通行證。

注意:如果你會不斷地添加/從existingHoldings刪除項目,那麼你可以使用SortedDictionary這應該是更快(排序列表是訪問元素較快,但需要更長的時間添加/刪除)

編輯:要注意這一點很重要LINQ用於搜索集合,而不是更新它們。因此,您可以使用LINQ來查找existsToAdd,它們在existingHoldings中不存在,然後通過existingHoldings循環設置基金,並在需要時設置Percentage,但是然後需要對holdingsToAdd和existingHoldings進行排序,您仍然可以循環每次收集一次。這將是O(2 * m * log n + n)的順序。編譯器可能能夠將這兩個查詢合併爲一個調用,但即使這樣,您也會看到類似的性能,但可讀性較差。

1

具有功能變異原始列表使得它非常非LINQ的,所以這裏是治療兩個列表作爲永恆不變的一個版本:

private List<Holding> CombineHoldings(
    List<Holding> holdingsToAdd, 
    List<Holding> existingHoldings) 
{ 
    var holdings = existingHoldings.Concat(holdingsToAdd) 
     .GroupBy(h => h.Sector) 
     .Select(g => 
     { 
      var result = g.First(); 
      result.Percentage = g.Select(h => h.Percentage).Sum(); 
      return result; 
     }); 
    return holdings.ToList(); 
} 

Definintely不會贏得比賽的表現,但我喜歡它的簡單性。下面可能會更快,但更復雜,需要你要麼凌駕於控股平等比較部門或創建IEqualityComparer<Holding>

private List<Holding> CombineHoldings(
    List<Holding> holdingsToAdd, 
    List<Holding> existingHoldings) 
{ 
    var holdings = existingHoldings.GroupJoin(holdingsToAdd, h => h, h => h, 
     (h, toAdd) => 
     new Holding(
      h.Sector, 
      /*Other parameters to clone*/, 
      h.Percentage + toAdd.Select(i => i.Percentage).Sum()) 
     ).ToList(); 
    holdings.AddRange(holdingsToAdd.Except(holdings)); 
    return holdings; 
}; 
相關問題