2013-06-21 48 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