C# 比较更改对象列表的子列表(以Linq为单位)

C# 比较更改对象列表的子列表(以Linq为单位),c#,linq,list,C#,Linq,List,我有一个对象列表,它有一个字符串的子列表,这些结构可以在几天之间改变,我想比较一下它们,看看是否有变化 public class Recipe { public string ID { get; set; } public string Name { get; set; } public List<string> Ingredients { get; set; } } 我试图找到所有在几天之间成分发生变化的配方。我已经能够提出一个Linq语句来查找新的Re

我有一个对象列表,它有一个字符串的子列表,这些结构可以在几天之间改变,我想比较一下它们,看看是否有变化

public class Recipe
{
    public string ID { get; set; }
    public string Name { get; set; }
    public List<string> Ingredients { get; set; }
} 
我试图找到所有在几天之间成分发生变化的
配方。我已经能够提出一个Linq语句来查找新的
Recipe
s,这些新的
s位于列表2中,而不是列表1中

var newRecipes = list1.Where(x => !list2.Any(x1 => x1.ID == x.ID))
    .Union(list2.Where(x => !list1.Any(x1 => x1.ID == x.ID)));
但是,我还没有弄清楚如何只选择在列表之间发生了
成分变化的
配方

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))));
如何获取在字符串子列表中发生更改的对象列表?

当然,您可以这样做。首先获取(不使用它也可以完成,但是效率稍低,如果需要,请询问我)。但是,在此之前,我们需要添加一个更好的可枚举
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;
    }
}
公共类枚举比较程序:IEqualityComparer
{
public void等于(IEnumerable x,IEnumerable y)
{
返回x.x(y);
}
public int GetHashCode(IEnumerable obj)
{
int x=31;
foreach(obj中的T元素)
{
x+=37*element.GetHashCode();
}
返回x;
}
}
在这之后,事情就这么简单了:

var modifiedRecipes = list2.ExceptBy(list1, n => n.Ingredients,
    new EnumerableComparer<string>());
var modifiedRecipes=list2.ExceptBy(list1,n=>n.components,
新的EnumerableComparer());
如果您打算在代码中使用这个,我不会一直实例化一个新的EnumerableComparer,因为它们都是一样的。对每种类型使用相同的实例。

当然,您可以这样做。首先,获取(不使用它也可以完成,但是效率稍低,如果您需要,请询问我)。但是,在此之前,我们需要添加一个更好的可枚举
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;
    }
}
公共类枚举比较程序:IEqualityComparer
{
public void等于(IEnumerable x,IEnumerable y)
{
返回x.x(y);
}
public int GetHashCode(IEnumerable obj)
{
int x=31;
foreach(obj中的T元素)
{
x+=37*element.GetHashCode();
}
返回x;
}
}
在这之后,事情就这么简单了:

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

如果您打算在代码中使用这个,我不会一直实例化一个新的EnumerableComparer,因为它们都是一样的。对每种类型使用相同的实例。

此代码将为您获得具有相同ID的每对不匹配的接收器。您必须对成分列表进行排序,以获得与工作相同的序列

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 };

此代码将为您获得具有相同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 };

谢谢你的帮助。这似乎返回列表1中的所有
配方
s,而不是已更改的配方。我应该做些什么额外的比较呢?这个答案是错误的
GetHashCode
在不同的
Components
对象上总是不同的,因为它只检查引用的相等性(因此从不调用
Equals
)。您可以使用例如
return obj.Aggregate(13,(acc,item)=>acc*17+item.GetHashCode())来解决这个问题谢谢你的帮助。这似乎返回列表1中的所有
配方
s,而不是已更改的配方。我应该做些什么额外的比较呢?这个答案是错误的
GetHashCode
在不同的
Components
对象上总是不同的,因为它只检查引用的相等性(因此从不调用
Equals
)。您可以使用例如
return obj.Aggregate(13,(acc,item)=>acc*17+item.GetHashCode())来解决这个问题如果有更好的选项,这是非常低效的。@newStackExchangeInstance事实上,它比您的效率要高得多,因为您不必首先检查ID是否相等以消除具有不同ID的重复项。您的解决方案将运行更多的
SequenceEquals
(还有多少取决于
ExceptBy
的实现,这是不必要的第三方代码)ExceptBy是Jon Skeet的代码。/programmingmeme实际上,我的是~O(m+n),因为ExceptBy在内部使用哈希集,因此只检查是否存在哈希桶冲突。你的类似于O(mn)还有一点。@newStackExchangeInstance即使在我建议的修复之后,您的解决方案的性能也不如我的。我的解决方案的灵活性远不及您声称的O(nm)。请注意,我首先在ID上执行联接,这是使用哈希集完成的,复杂性为O(n+m).然后,对于ID相等的每一对元素,我都会检查序列是否相等。真的吗?还有,如果你试图优化我的代码(你没有),您不仅会意识到哈希集依赖于质数可分性的一致性,其中一些是17和13。此外,与foreach循环相比,聚合速度较慢。此外,不要在循环的每次运行中实例化一个新的
EnumerableComparer
。这样,我的解决方案更快。请参见此处的代码:这是非常低效的n有更好的选项可用。@newStackExchangeInstance实际上,它比您的更有效,因为您不必首先检查ID是否相等以消除具有不同ID的重复项。您的解决方案将运行更多的
SequenceEquals
(还有多少取决于
ExceptBy
的实现,这是不必要的第三方代码)ExceptBy是Jon Skeet的代码。/programmingmeme实际上,我的是~O(m+n),因为ExceptBy在内部使用哈希集,因此只检查是否存在哈希桶冲突。你的类似于O(mn)还有一点。@newStackExchangeInstance即使在我建议的修复之后,您的解决方案的性能也不如我的解决方案,这可以从以下几点得到证明。我的解决方案的灵活性远不及您声称的O(nm)。请注意,我首先执行的是joi