I have a class as follows:
public class Tag {
public Int32 Id { get; set; }
public String Name { get; set; }
}
And I have two lists of tag:
List<Tag> tags1;
List<Tag> tags2;
I used LINQ's select to get the Ids of each tags list. And then:
List<Int32> ids1 = new List<Int32> { 1, 2, 3, 4 };
List<Int32> ids2 = new List<Int32> { 1, 2, 3, 4 };
List<Int32> ids3 = new List<Int32> { 2, 1, 3, 4 };
List<Int32> ids4 = new List<Int32> { 1, 2, 3, 5 };
List<Int32> ids5 = new List<Int32> { 1, 1, 3, 4 };
ids1 should be equal to ids2 and ids3 ... Both have the same numbers.
ids1 should not be equal to ids4 and to ids5 ...
I tried the following:
var a = ints1.Equals(ints2);
var b = ints1.Equals(ints3);
But both give me false.
What is the fastest way to check if the lists of tags are equal?
UPDATE
I am looking for POSTS which TAGS are exactly the same as the TAGS in a BOOK.
IRepository repository = new Repository(new Context());
IList<Tags> tags = new List<Tag> { new Tag { Id = 1 }, new Tag { Id = 2 } };
Book book = new Book { Tags = new List<Tag> { new Tag { Id = 1 }, new Tag { Id = 2 } } };
var posts = repository
.Include<Post>(x => x.Tags)
.Where(x => new HashSet<Int32>(tags.Select(y => y.Id)).SetEquals(book.Tags.Select(y => y.Id)))
.ToList();
I am using Entity Framework and I get the error:
An exception of type 'System.NotSupportedException' occurred in mscorlib.dll but was not handled in user code Additional information: LINQ to Entities does not recognize the method 'Boolean SetEquals(System.Collections.Generic.IEnumerable`1[System.Int32])' method, and this method cannot be translated into a store expression.
How do I solve this?
ids5
contains duplicates. Is that intentional?
[EF]
tag, and make sure that the title of the new question says "comparing lists inside EF's Where clause" or something similar.
Use SequenceEqual
to check for sequence equality because Equals
method checks for reference equality.
var a = ints1.SequenceEqual(ints2);
Or if you don't care about elements order use Enumerable.All
method:
var a = ints1.All(ints2.Contains);
The second version also requires another check for Count
because it would return true even if ints2
contains more elements than ints1
. So the more correct version would be something like this:
var a = ints1.All(ints2.Contains) && ints1.Count == ints2.Count;
In order to check inequality just reverse the result of All
method:
var a = !ints1.All(ints2.Contains)
List<T>
equality does not check them element-by-element. You can use LINQ's SequenceEqual
method for that:
var a = ints1.SequenceEqual(ints2);
To ignore order, use SetEquals
:
var a = new HashSet<int>(ints1).SetEquals(ints2);
This should work, because you are comparing sequences of IDs, which do not contain duplicates. If it does, and you need to take duplicates into account, the way to do it in linear time is to compose a hash-based dictionary of counts, add one for each element of the first sequence, subtract one for each element of the second sequence, and check if the resultant counts are all zeros:
var counts = ints1
.GroupBy(v => v)
.ToDictionary(g => g.Key, g => g.Count());
var ok = true;
foreach (var n in ints2) {
int c;
if (counts.TryGetValue(n, out c)) {
counts[n] = c-1;
} else {
ok = false;
break;
}
}
var res = ok && counts.Values.All(c => c == 0);
Finally, if you are fine with an O(N*LogN)
solution, you can sort the two sequences, and compare them for equality using SequenceEqual
.
SequenceEqual
the elements must be in the same order - OP wants the same elements in any order.
{1, 1, 2, 3}
would be "equal" to {1, 2, 2, 3}
bool isEqual = ids1.Count == ids2.Count; if (isEqual) isEqual = ids1.OrderBy(i=>i).SequenceEqual(ids2.OrderBy(i => i));
.
Enumerable.SequenceEqual(FirstList.OrderBy(fElement => fElement),
SecondList.OrderBy(sElement => sElement))
id
in the OP's context, or element
in a generic context.
.Distinct()
before the OrderBy
s if you want to compare and ignore duplicates. This is definitely not the most efficient method of comparing sets. HashSet.SetEquals ignores duplicates and order. Enumerable.SequenceEquals considers duplicates and preserves order.
Success story sharing
[1, 1, 2] != [1, 2, 2]
.All
with a lambda like.All(i=> ints2.Contains(i))
, but since list.Contains() matches the function signature of taking aint
and returning abool
, then he is passing the function name directly as a predicate. Essentially the same asints1.All(i=> ints2.Contains(i))
. Just wanted to point this out in case others like me were initially confused.var a = ints1.All(ints2.Contains) && ints1.Count == ints2.Count;
tovar a = ints1.Count == ints2.Count && ints1.All(ints2.Contains);
. A simple count comparison is likely much faster than the.All
call. If counts are not equal it would return faster.