2015-11-04 57 views
5

我想問一下是否有一種優雅而有效的方式將兩個MyClass列表合併爲一個?根據屬性將兩個列表合併爲一個

MyClass的是這樣的:

  • ID:int
  • 名稱:string
  • EXTID:int?

和列表是根據列表中的不同來源和對象填充做份額ID,所以它看起來像這樣:

MyClass instance from List1 
ID = someInt 
Name = someString 
ExtID = null 

而且從列表2

MyClass的實例
ID = someInt (same as List1) 
Name = someString (same as List1) 
ExtID = someInt 

我基本上需要的是這兩個清單合併,所以結局是包含一個列表:

ID = someInt (from List1) 
Name = someString (from List1) 
ExtID = someInt (null if no corresponding item - based on ID - on List2) 

我知道我能做到這一點簡單地使用foreach循環,但我很想知道是否有更優雅和更可取的(由於性能,可讀性)方法?

+0

編寫一個'Merge'函數,將兩個具有相同'ID'的實例組合成一個 - 然後你可以連接列表,按ID編組並最終使用合併函數摺疊/縮小組 - 這就是我將如何做到的爲readabi lity - 對於性能來說,你可能在排序和循環時很好 – Carsten

回答

1

有很多方法取決於什麼是優先級,例如。聯盟+查找:

//this will create a key value pairs: id -> matching instances 
var idMap = list1.Union(list2).ToLookup(myClass => myClass.ID); 
//now just select for each ID the instance you want, ex. with some value 
var mergedInstances = idMap.Select(row => 
     row.FirstOrDefault(myClass => myClass.ExtId.HasValue) ?? row.First()); 

以上的好處是,它會與,即使它們包含許多重複的isntances無論任何金額列出工作,那麼你就可以很容易地修改合併

小的提升條件將提取合併實例的方法:

MyClass MergeInstances(IEnumerable<MyClass> instances){ 
    return instances.FirstOrDefault(myClass => myClass.ExtId.HasValue) 
      ?? instances.First(); //or whatever else you imagine 
} 

,現在只用它在上面的代碼中

var mergedInstances = idMap.Select(MergeInstances); 

清潔,靈活,簡單,無附加條件。性能不明智,但誰在乎。

編輯:由於性能的優先級,多了一些選擇

  1. 進行查找像上面但只適用於較小的列表。然後遍歷更大的並做所需的更改O(m log m)+ O(n)。 m - 更小的列表大小,n更大的列表大小 - 應該是最快的。

  2. 按元素ID排序這兩個列表。創建一個for循環,迭代它們兩個,使兩個列表的當前索引保持爲具有相同id的元素。將索引移動到在這兩個列表中找到的下一個最小的標識符,如果只有一個標識符,則只將其移入。O(n log n)+ O(m log m)+ O(n);

+0

它會比List2上的foreach循環更快地運行到List1中的相應項目並設置值? ;>如果沒有,那麼因爲它隱藏在庫的私有方法中,所以我不介意使用foreach循環來獲得更多的性能,因爲我不知道列表可以增長多少,以及這將使用多長時間。我可能已經說錯了,但優先考慮的是性能。 – user1970395

+1

如果對於每個元素需要搜索其他列表的元素,那麼你會得到O(n^2)的複雜性,因爲查找是O(log n)* O(n)Select這實際上是一個foreach循環O(n log n)對O(n ^)。我的勝利;)。無論如何,使用查找表(字典/地圖)是一種方法。而且你也獲得了靈活性。如果你可以改進現有的解決方案,你可以考慮從頭開始提供一個字典,而不是列表。 – mikus

+1

您可以在列表中的一個上創建查找(或字典),然後使用查找快速查找第二個查找。 你也可以考慮訂購這兩個清單,並做一個智能循環,只有一次通過兩個清單O(n log n)+ O(n)。 – mikus

-1

我sugest建立在類的方法foreach循環,所以每次你需要做你會使用類似

instanceList1.MergeLists(instanceList2) 

,並用這種方法,你可以這樣的事情控制合併操作所需的所有內容。

+0

@mikus LINQ * does *使用迭代和迭代器,所以關於'foreach'的註釋不適用。另一方面,當OP詢問如何編寫該函數時,「編寫自己的函數」並不是一個好的答案。也許這應該被刪除並重新張貼爲評論? –

+0

其關於很好的解決方案不是一個不會在內部使用循環的解決方案... – mikus

+0

嗯,我只是假定他知道如何去做這個功能,我認爲這應該是更好的方法。 (他說他不想要這樣的foreach是的,但我認爲他的意思是,他不想每次需要合併時都這樣做) –

1

這是你想要的

var joined = from Item1 in list1 
     join Item2 in list2 
     on Item1.Id equals Item2.Id // join on some property 
     select new MyClass(Item1.Id, Item1.Name, Item1.ExtID??Item2.ExtID); 

編輯:如果你正在尋找一個外部聯接,

var query = from Item1 in list1 
      join Item2 in list2 on Item1.Id equals Item2.Id into gj 
      from sublist2 in gj.DefaultIfEmpty() 
      select new MyClass(Item1.Id, Item1.Name, sublist2??string.empty); 

可讀性明智的,使用foreach循環是不是太糟糕的主意..

+0

因爲它是內部連接,如果某些id是缺少其中一個列表 – mikus

+0

yeah..true..added outer join case – Godsent