1

I don't know where I make a mistake, but i cannot compare two lists of lists of (for example) integers.

Simple example:

List<List<int>> A = new List<List<int>>();
A.Add(new List<int>(new int[] { 1 }));
A.Add(new List<int>(new int[] { 2 }));

List<List<int>> B = new List<List<int>>();
B.Add(new List<int>(new int[] { 1 }));

if (A.Contains(B[0])){
    Console.WriteLine("TRUE");
else{
    Console.WriteLine("FALSE");
}

returns False. What is the correct way to compare in this kind of situation? I tried also Intersect and Except with same result.

Iver
  • 32
  • 1
  • 6
  • What are you trying to achieve with `A.Contains(B[0])`? Do you want this to tell you whether any of the lists in A have exactly the same contents as the list provided? Identical elements in the same order? Does order matter? What if the list in B is a subset of a list in A? Should that be true or false? – yoozer8 Feb 20 '14 at 20:32
  • It's false because it is not the same list reference (It compares the list directly, not only the elements in it). – Pierre-Luc Pineault Feb 20 '14 at 20:32
  • The outer `List<>` is not relevant here, your core problem is comparing 2 `List`. And is `{1, 2}` equal to `{2, 1}` ? – H H Feb 20 '14 at 20:32
  • In my case 'equal' means 'same elements in same order'. – Iver Feb 20 '14 at 20:42

5 Answers5

4

It might help to visualize this:

A:
   A[0]:
       A[0][0]: 1
   A[1]:
       A[1][0]: 2
B:
   B[0]:
       B[0][0]: 1

When you call A.Contains(), you're asking whether the thing you're testing is either A[0] or A[1]. Since you're passing it B, and B is neither of those two, it returns false. Even if you were to pass it B[0] or B[0][0], you'd still get false, because none of those are the same object as A[0] or A[1].

SequenceEqual() is the function which will test whether the contents of two List are the same. So you want to test whether either A[0] or A[1] is SequenceEqual to B[0] (i.e. either A[0][0] == B[0][0] or A[1][0] == B[0][0]).

The best way to do this is with the LINQ function .Any(). A.Any(a => a.SequenceEqual(B[0])) will test B[0] against everything in A. If you want to compare all B elements to all A elements, you'd need something more like A.Any(a => B.Any(b => a.SequenceEqual(b)). This can be translated as:

foreach (var a in A) // A[0], A[1], etc.
{ 
    foreach (var b in B) // B[0], B[1], etc.
    {
       // is our current a value matching the current b value?
       // i.e. A[0] is exactly the same as B[0] on the first pass
       if (a.SequenceEqual(b)) return true; 
    }
}
return false;
Bobson
  • 13,498
  • 5
  • 55
  • 80
  • I suspect, that problem is caused by some kind of reference equality, but i hadn't got any idea how to solve it. Thanks for explanation. – Iver Feb 20 '14 at 20:50
3

you can iterate through each list in A and compare it to B[0] with SequenceEqual:

if (A.Any(list => list.SequenceEqual(B[0])))
{
    Console.WriteLine("TRUE");
}
else
{
     Console.WriteLine("FALSE");
}

will return true

Jonesopolis
  • 25,034
  • 12
  • 68
  • 112
3
bool contains = A.Any(a => B.Any(b => b.OrderBy(x => x)
                                       .SequenceEqual(a.OrderBy(x => x))));

 [[1],[2]]    and [[3]]       => false
 [[1],[2]]    and [[1]]       => true
 [[2],[1]]    and [[1],[2]]   => true
 [[3],[4]]    and [[1],[2]]   => false
 [[1,2],[3]]  and [[2,1],[4]] => true
L.B
  • 114,136
  • 19
  • 178
  • 224
  • @Jonesy Question is not clear. I assumed `[2,1]` is equal to `[1,2]`. Otherwise `SequenceEqual` would return false. – L.B Feb 20 '14 at 21:23
0

For reference types other than string, operator == returns true if its two operands refer to the same object. That's clearly not the case here you are comparing one list reference to another list reference.

Peter R
  • 2,865
  • 18
  • 19
  • It actually (surprisingly?) does the same thing for `string` if it is passed as generic parameter. – Grozz Feb 20 '14 at 20:38
  • @Grozz - the `==` is applied to the `List<>`s, not to the `T`s – H H Feb 21 '14 at 18:14
  • @HenkHolterman I understand. I'm just saying that if you have generic `Some` and compare its parameter with `==` it will use referential equality both for `T <- List` and `T <- string` – Grozz Feb 21 '14 at 19:10
0

You are comparing lists, which by default are compared for reference equality. For this particular case you can use SequenceEqual extension in the following way:

bool contains = A.Any(a => a.SequenceEqual(B[0]));
Console.WriteLine(contains);  

This may or may not be efficient enough for your purposes :)

Another way to approach this is to write your own IEqualityComparer wrapper around SequenceEqual as described here: IEqualityComparer for SequenceEqual and then use it as parameter in your .Contains call.

Community
  • 1
  • 1
Grozz
  • 8,317
  • 4
  • 38
  • 53