Reputation: 16245
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?
Upvotes: 0
Views: 1057
Reputation: 22794
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.
Upvotes: 2
Reputation: 13817
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 };
Upvotes: 3