2013-06-26 190 views
28

說我有以下幾點:比較兩個列表通過一個物業使用LINQ

class Widget1{ 
     public int TypeID { get; set; } 
     public string Color { get; set; } 
    } 

    class Widget2 
    { 
     public int TypeID { get; set; } 
     public string Brand { get; set; } 
    } 

    private void test() 
    { 
     List<Widget1> widgets1 = new List<Widget1>(); 
     List<Widget2> widgets2 = new List<Widget2>(); 
     List<Widget1> widgets1_in_widgets2 = new List<Widget1>(); 

     //some code here to populate widgets1 and widgets2 

     foreach (Widget1 w1 in widgets1) 
     { 
      foreach (Widget2 w2 in widgets2) 
      { 
       if (w1.TypeID == w2.TypeID) 
       { 
        widgets1_in_widgets2.Add(w1); 
       } 
      } 
     } 
    } 

我使用兩個foreach循環通過TYPEID到列表比較來填充榜第三。 是否有任何其他方式使用LINQ通過TypeID比較這兩個列表?也許使用Interstect或其他功能?

回答

33

這裏你想要的是一個Join

var widgets1_in_widgets2 = from first in widgest1 
    join second in widgets2 
    on first.TypeID equals second.TypeID 
    select first; 

Intersect可以是作爲Join一個特殊情況,其中兩個序列是相同類型的更多或更少的思想,並且因此可以應用於平等代替需要的投影爲每種類型來生成密鑰的比較。鑑於你的情況,Intersect不是一個選項。

如果某個ID已在第二組複製的,你不希望項目的成果進行復制,然後你可以使用的GroupJoin代替Join

var widgets1_in_widgets2 = from first in widgest1 
    join second in widgets2 
    on first.TypeID equals second.TypeID 
    into matches 
    where matches.Any() 
    select first; 
+0

+1 _「鑑於你的情況下,相交是不是一種選擇」 _這將是一種選擇時,定製'的IEqualityComparer '將是可能的,所以一個普通的基礎類型。然後,您可以將其傳遞給[this overload](http://msdn.microsoft.com/en-us/library/bb355408.aspx),也可以重寫'BaseWidget'中的'Equals' +'GetHashCode'以通過TypeID進行比較'默認。 –

3

加入具有以下缺點:如果widgets1或widgets2包含多個具有相同TypeID的元素(順便說一下,這也適用於您的原始代碼),那麼您的結果可能會重複。

以下內容將完全按照您的要求進行操作:返回widgets1中的所有元素,其中具有相應TypeID的元素存在於widgets2中。

widgets1_in_widgets2 = (from w1 in widgets1 
         where widgets2.Any(w2 => w1.TypeID == w2.TypeID) 
         select w1).ToList() 
+1

儘管這會生成正確的輸出,但它會對性能相當差,因爲您正在對另一個集合中的每個項目的集合進行線性搜索。 – Servy

+0

'「連接的缺點是,如果widgets1或widgets2包含具有相同TypeID多於一個的元素,則結果可能會重複。」OP中的代碼實際上具有與* Join *完全相同的輸出,所以要麼他沒有在任何列表中有重複的鍵,要麼他*想要*重複的結果。 – Servy

+0

@Servy:好的,我會在我的回答中提及。不過,變量命名使我認爲這是無意的。 – Heinzi

28

你可以做到這一點

widgets2.Where(y=>widget1.Any(z=>z.TypeID==y.TypeID)); 
+4

儘管這會生成正確的輸出,但它會在您爲集合中的其他集合中的每個項目執行線性搜索時具有相當差的性能。 – Servy

+0

@Servy hmmm..indeed – Anirudha

+1

最簡單的修改就是將其中一個集合的TypeID存儲在一個'HashSet'中,這個集合可以更快地被搜索到。正如我所展示的,更加有意義的變化是僅僅使用'Join',儘管它最終會在底層做相同的事情。 – Servy

2

嘗試使用過載「去哪兒」

var isMatch = !widgets1.Where((w1, index) => w1.TypeId == widgets2[index].TypeId)).Any(); 
+1

只是代碼而沒有解釋的答案,特別是當問題已被徹底解答之前 – ItamarG3

+0

您可以更新此答案以指定它的不同之處。我發現這是一個有用的建議解決方案,在您想要比較順序並避免任何線性搜索的情況下。 – DannyMeister

0

我喜歡這個解決方案,因爲它是簡單的代碼來讀取。

bool result = firstList.All(o => secondList.Any(w => w.Prop1 == o.Prop1 && w.Prop2 == o.Prop2)); 

見小提琴的完整的例子:Fiddle example comparation

+0

Thx,這對我有用。如果(projectIds.All(y => r.ProjectsId.Any(z => z.Equals(y)))) reports.Add(r); } – Parveen