2013-06-21 55 views
0

我有一個包含字符串子對象列表的對象列表,並且這些結構可以在幾天之間更改,我期待比較它們以查看是否進行了更改。比較在Linq中對象列表的子列表

public class Recipe 
{ 
    public string ID { get; set; } 
    public string Name { get; set; } 
    public List<string> Ingredients { get; set; } 
} 

ID字段列表的版本和Ingredients之間的相同僅僅是一個字符串的名單。

List<Recipe> list1 = GetRecipes("2013-06-20"); 
List<Recipe> list2 = GetRecipes("2013-06-21"); 

我試圖找到所有Recipe之間有天成分變化。我已經能夠拿出一個LINQ語句來找到新的Recipe s表示通過這樣的

var newRecipes = list1.Where(x => !list2.Any(x1 => x1.ID == x.ID)) 
    .Union(list2.Where(x => !list1.Any(x1 => x1.ID == x.ID))); 

但不是列表1列表2但是,我還沒有想出如何只選擇Recipe s,表中列出了Ingredient之間的變化。

var modifiedRecipes = list1.Where(x => !list2.Any(x1 => x1.ID == x.ID && x1.Ingedients.SequenceEqual(x.Ingedients))) 
    .Union(list2.Where(x => !list1.Any(x1 => x1.ID == x.ID && x1.Ingedients.SequenceEqual(x.Ingedients)))); 

如何獲取已在字符串子列表中進行更改的對象列表?

回答

3

此代碼將爲您提供配對的每個不匹配的相同ID的匹配。您必須對SequenceEqual的成分列表進行排序才能正常工作。

var changed = from older in GetRecipes("2013-06-20") 
       join newer in GetRecipes("2013-06-21") on older.ID equals newer.ID 
       where !older.Ingredients.SequenceEquals(newer.Ingredients) 
       select new { older, newer }; 
+0

如果有更好的選擇,這是非常低效的。 –

+0

@newStackExchangeInstance實際上,它比你更有效率,因爲你不首先檢查ID是否相等,以消除具有不同ID的收件人。你的解決方案會運行更多的'SequenceEquals'(還有多少取決於'ExceptBy'的實現,這是不必要的第三方代碼) – ghord

+0

ExceptBy是Jon Skeet的代碼。/programmingmeme實際上,我的是O(m + n),因爲ExceptBy在內部使用了一個hashset,因此只會檢查是否存在hashbucket衝突。你的是O(mn)和更多一點。 –

2

當然,你可以做到這一點。首先獲得MoreLINQ(沒有它可以完成,但它效率稍低,請問我是否需要它)。然而,在此之前,我們需要添加一個更好的枚舉IEqualityComparer

public class EnumerableComparer<T> : IEqualityComparer<IEnumerable<T>> 
{ 
    public void Equals(IEnumerable<T> x, IEnumerable<T> y) 
    { 
     return x.SequenceEqual(y); 
    } 
    public int GetHashCode(IEnumerable<T> obj) 
    { 
     int x = 31; 
     foreach (T element in obj) 
     { 
      x += 37 * element.GetHashCode(); 
     } 
     return x; 
    } 
} 

之後,它是如此簡單:

var modifiedRecipes = list2.ExceptBy(list1, n => n.Ingredients, 
    new EnumerableComparer<string>()); 

如果你要使用這個在你的代碼,我不會不會一直實例化一個新的EnumerableComparer,因爲它們都是一樣的。爲每種類型使用相同的實例。

+0

謝謝你的幫助。這似乎會返回_list1_中的所有'配方',而不是那些發生變化的配方。有什麼額外的比較我應該做的? – Kirk

+0

@Kirk呃。我排除了錯誤的方式。固定。 –

+0

這個答案不正確。 'GetHashCode'在不同的'Ingredients'對象上總會不同,因爲它只檢查引用的相等性(因此'Equals'永遠不會被調用)。你可以通過使用例如'return obj.Aggregate(13,(acc,item)=> acc * 17 + item.GetHashCode());' – ghord