Most Efficient Way to Compare Two Ienumerables (Or Lists) in Linq

C#: Compare contents of two IEnumerables

There are quite a few ways. Assume A and B is IEnumerable.

!A.Except(B).Any() && !B.Except(A).Any()
A.Count() == B.Count() && A.Intersect(B).Count() == B.Count()
etc

What is the shortest way to compare if two IEnumerable<T> have the same items in C#?

Even if the order doesn't matter to you, it doesn't rule out SequenceEqual as a viable option.

var lst1 = new [] { 2,2,2,2 };
var lst2 = new [] { 2,3,4,5 };
var lst3 = new [] { 5,4,3,2 };

//your current function which will return true
//when you compare lst1 and lst2, even though
//lst1 is just a subset of lst2 and is not actually equal
//as mentioned by Wim Coenen
(lst1.Count() == lst2.Count() &&
!lst1.Except(lst2).Any()); //incorrectly returns true

//this also only checks to see if one list is a subset of another
//also mentioned by Wim Coenen
lst1.Intersect(lst2).Any(); //incorrectly returns true

//So even if order doesn't matter, you can make it matter just for
//the equality check like so:
lst1.OrderBy(x => x).SequenceEqual(lst2.OrderBy(x => x)); //correctly returns false
lst3.OrderBy(x => x).SequenceEqual(lst2.OrderBy(x => x)); // correctly returns true

Most efficient way to compare two generic lists based on id elements contained within nested list (C#)

Your current algorithm seem to be O(n*m*s*s) where n = number of existing items, m = number number of potential matches and s = average number of suppliers for each existingItem/PotentialMatch. You could reduce the running time to O(n*m*s) by using a hash-set for the matching of suppliers.

A generic version would look like this

public static IEnumerable<(T1, T2)> SetJoin<T1, T2, TKey>(
IEnumerable<T1> t1s,
IEnumerable<T2> t2s,
Func<T1, IEnumerable<TKey>> t1Key,
Func<T2, IEnumerable<TKey>> t2Key) where TKey : IEquatable<TKey>
{
foreach (var t1 in t1s)
{
var t1Keys = new HashSet<TKey>(t1Key(t1));
foreach (var t2 in t2s)
{
// t2Key(t2) would be called many times,
// might be worth pre-computing it for each t2.
if (t2Key(t2).Any(t1Keys.Contains))
{
yield return (t1, t2);
}
}
}
}

And call it like

SetJoin<ExistingItems, PotentialMatches, int>(
existingItems,
potentialMatches,
e=> e.Suppliers.Select(s => s.Id),
p => p.Suppliers.Select(s => s.Id))

Also, while linq result in compact and nice code, it is often faster to write the equivalent logic using regular loops if performance is important.

Better way of comparing two lists with LINQ?

I would do this:

var map = allElements.ToDictionary(x => x.Id);    
if (!someElements.All(id => map.ContainsKey(id))
{
// Return early
}
var list = someElements.Select(x => map[x])
.ToList();

Note that the first line will throw an exception if there are any duplicates in allElements.

How to compare two lists and change one property

Given that your target list is named targetDocs and the list you want to check for document existance is srcDocs try something like (don't have access to a compiler here so can't test):

targetDocs.ForEach(d => d.IsActive = srcDocs.Any(sd => sd.id == d.Id))

I'm assuming that we are talking about Lists and not other collection types as the ForEach extension method is defined for Lists.

LINQ (or something else) to compare a pair of values from two lists (in any order)?

You can do in this way:

Define a custom IEqualityComparer<FooAttribute> :

class FooAttributeComparer : IEqualityComparer<FooAttribute>
{
public bool Equals(FooAttribute x, FooAttribute y)
{
return x.Match(y);
}
public int GetHashCode(FooAttribute obj)
{
return 0;
// This makes lookups complexity O(n) but it could be reasonable for small lists
// or if you're not sure about GetHashCode() implementation to do.
// If you want more speed you could return e.g. :
// return obj.Field1.GetHashCode() ^ (17 * obj.Field2.GetHashCode());
}
}

Define an extension method to compare lists in any order and having the same number of equal elements:

public static bool ListContentIsEqualInAnyOrder<T>(
this IEnumerable<T> list1, IEnumerable<T> list2, IEqualityComparer<T> comparer)
{
var lookup1 = list1.ToLookup(x => x, comparer);
var lookup2 = list2.ToLookup(x => x, comparer);
if (lookup1.Count != lookup2.Count)
return false;
return lookup1.All(el1 => lookup2.Contains(el1.Key) &&
lookup2[el1.Key].Count() == el1.Count());
}

Usage example:

static void Main(string[] args)
{
List<FooAttribute> attrs = new List<FooAttribute>
{
new FooAttribute(typeof(int), typeof(double)),
new FooAttribute(typeof(int), typeof(double)),
new FooAttribute(typeof(bool), typeof(float)),
new FooAttribute(typeof(uint), typeof(string)),
};
List<FooAttribute> attrs2 = new List<FooAttribute>
{
new FooAttribute(typeof(uint), typeof(string)),
new FooAttribute(typeof(int), typeof(double)),
new FooAttribute(typeof(int), typeof(double)),
new FooAttribute(typeof(bool), typeof(float)),
};

// this returns true
var listEqual1 = attrs.ListContentIsEqualInAnyOrder(attrs2, new FooAttributeComparer());

// this returns false
attrs2.RemoveAt(1);
var listEqual2 = attrs.ListContentIsEqualInAnyOrder(attrs2, new FooAttributeComparer());
}


Related Topics



Leave a reply



Submit