简体   繁体   中英

Compare sublist of a list for objects that change, in Linq

I have a List of objects that has a sublist of strings and these structures can change between days and I'm looking to compare them to see if a change was made.

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

The ID field is the same between versions of the list and Ingredients is just a list of strings .

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

I'm trying to find all Recipe s that have ingredient changes between days. I've been able to come up with a Linq statement to find new Recipe s that are in list2 but not list1 by something like this

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

But, I haven't figured out yet how to select only Recipe s that have had Ingredient changes between lists.

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

How can I get a list of objects that have had changes in a sublist of strings?

This code will get you pairs of every non matching recepies of the same ID. You have to sort the ingredients lists for SequenceEqual to work though.

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

Sure, you can do that.Get MoreLINQ first of all (it can be done without it, however it's slightly more inefficient, ask me if you want it). However, before that we need to add a better enumerable 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;
    }
}

After that it's as simple as this:

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

If you're going to be using this around your code, I wouldn't instantiate a new EnumerableComparer all the time, as they're all the same. Use the same instance for each type.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM