66

Other than stepping through the elements one by one, how do I compare two lists of strings for equality (in .NET 3.0):

This fails:

// Expected result.
List<string> expected = new List<string>();
expected.Add( "a" );
expected.Add( "b" );
expected.Add( "c" );

// Actual result
actual = new List<string>();
actual.Add( "a" );
actual.Add( "b" );
actual.Add( "c" );

// Verdict
Assert.IsTrue( actual == expected );
Donald Duck
  • 8,409
  • 22
  • 75
  • 99
Adam Kane
  • 3,996
  • 7
  • 44
  • 53
  • possible duplicate of [What is the best way to check two List lists for equality in C#](http://stackoverflow.com/questions/876508/what-is-the-best-way-to-check-two-listt-lists-for-equality-in-c-sharp) – nawfal Jun 30 '15 at 00:42

9 Answers9

80

Try the following

var equal = expected.SequenceEqual(actual);

Test Version

Assert.IsTrue( actual.SequenceEqual(expected) );

The SequenceEqual extension method will compare the elements of the collection in order for equality.

See http://msdn.microsoft.com/en-us/library/bb348567(v=vs.100).aspx

Clement Bellot
  • 841
  • 1
  • 7
  • 19
JaredPar
  • 733,204
  • 149
  • 1,241
  • 1,454
44

Many test frameworks offer a CollectionAssert class:

CollectionAssert.AreEqual(expected, actual);

E.g MS Test

Michael Freidgeim
  • 26,542
  • 16
  • 152
  • 170
dahlbyk
  • 75,175
  • 8
  • 100
  • 122
  • 1
    Alas the messages it gives when it fails are slightly useless, eg. "Different number of elements" or "Element at index 0 do not match", without telling you *what they are*. – Colonel Panic May 15 '15 at 15:38
13

You can always write the needed function themselves:

public static bool ListEquals<T>(IList<T> list1, IList<T> list2) {
    if (list1.Count != list2.Count)
        return false;
    for (int i = 0; i < list1.Count; i++)
        if (!list1[i].Equals(list2[i]))
            return false;
    return true;
}

and use it:

// Expected result.
List<string> expected = new List<string>();
expected.Add( "a" );
expected.Add( "b" );
expected.Add( "c" );

// Actual result
actual = new List<string>();
actual.Add( "a" );
actual.Add( "b" );
actual.Add( "c" );

// Verdict
Assert.IsTrue( ListEquals(actual, expected) );
AndreyAkinshin
  • 18,603
  • 29
  • 96
  • 155
  • Nice! I was looking to avoid stepping through the elments, but that's a great generic way you wrote the method. – Adam Kane Oct 10 '09 at 03:56
  • 1
    Upvoted, this is a good answer. However, note that the function will throw an exception if either list is null. Recommend adding the following statements to the beginning of the function: `if (list1 == null && list2 == null) return true;` and `if (list1 == null || list2 == null) return false;` – Mike Taverne Nov 20 '15 at 18:09
10

I noticed no one actually told you why your original code didn't work. This is because the == operator in general tests reference equality (i.e. if the two instances are pointing to the same object in memory) unless the operator has been overloaded. List<T> does not define an == operator so the base reference equals implementation is used.

As other posters have demonstrated, you will generally have to step through elements to test "collection equality." Of course, you should use the optimization suggested by user DreamWalker which first tests the Count of the collections before stepping through them.

Community
  • 1
  • 1
bobbymcr
  • 23,769
  • 3
  • 56
  • 67
  • Thank you! I could think of several ways of doing the comparison myself, but when I ran into this issue, I thought "Well, it obviously isn't checking for equality, so what IS it doing?" – JHixson Feb 28 '14 at 16:24
9

If the order matters:

bool equal = a.SequenceEquals(b);

If the order doesn't matter:

bool equal = a.Count == b.Count && new HashSet<string>(a).SetEquals(b);
Gaui
  • 8,723
  • 16
  • 64
  • 91
2

Using Linq and writing the code as an extension method :

public static bool EqualsOtherList<T>(this List<T> thisList, List<T> theOtherList)
{
  if (thisList == null || theOtherList == null || 
      thisList.Count != theOtherList.Count) return false;
  return !thisList.Where((t, i) => !t.Equals(theOtherList[i])).Any();
}
Olivier
  • 79
  • 3
1

You could write an extension method like so:

public static class ListExtensions
    {
        public static bool IsEqual<T>(this IList<T> list,IList<T> target, IComparer<T> comparer) where T:IComparable<T>
        {
            if (list.Count != target.Count)
            {
                return false;
            }
            int index = 0;
            while (index < list.Count && 
                   comparer.Compare(list[index],target[index]) == 0)
            {
                index++;
            }
            if (index != list.Count)
            {
                return false;
            }
            return true;
        }
    }

And call it like so:

List<int> intList = new List<int> { 1, 234, 2, 324, 324, 2 };
List<int> targetList = new List<int> { 1, 234, 2, 324, 324 };
bool isEqual = intList.IsEqual(targetList, Comparer<int>.Default);

EDIT: Updated the code to use a static method instead since OP is using .NET 3.0

public static bool IsEqual<T>(IList<T> sourceList, IList<T> targetList, IComparer<T> comparer) where T : IComparable<T>
        {
            if (sourceList.Count != targetList.Count)
            {
                return false;
            }
            int index = 0;
            while (index < sourceList.Count &&
                   comparer.Compare(sourceList[index], targetList[index]) == 0)
            {
                index++;
            }
            if (index != sourceList.Count)
            {
                return false;
            }
            return true;
        }

Client:

        bool isEqual = IsEqual(intList,targetList, Comparer<int>.Default);
Abhijeet Patel
  • 6,562
  • 8
  • 50
  • 93
  • @Adam: I realised that you are using .NET 3.0 and not .NET 3.5 I'll edit my answer to make the code usable for .NET 3.0 by making it a static method instead of an extension method – Abhijeet Patel Oct 10 '09 at 04:57
0

While it does iterate over the collection, this extension method I created does not require the order of the two lists to be the same, and it works with complex types as well, as long as the Equals method is overridden.

The following two lists would return true:

List<string> list1 = new List<string>
{
    { "bob" },
    { "sally" },
    { "john" }
};

List<string> list2 = new List<string>
{
    { "sally" },
    { "john" },
    { "bob" }
};

Method:

public static bool IsEqualTo<T>(this IList<T> list1, IList<T> list2)
{
    if (list1.Count != list2.Count)
    {
        return false;
    }

    List<T> list3 = new List<T>();

    foreach (var item in list2)
    {
        list3.Add(item);
    }

    foreach (var item in list1)
    {
        int index = -1;
        for (int x = 0; x < list3.Count; x++)
        {
            if (list3[x].Equals(item))
            {
                index = x;
            }
        }

        if (index > -1)
        {
            list3.RemoveAt(index);
        }
        else
        {
            return false;
        }
    }

    return !list3.Any();
}
Johnie Karr
  • 2,744
  • 2
  • 35
  • 44
0

It might be used with not regular way but without implementation IEquatable for Custom types

JsonConvert.SerializeObject( myList1) == JsonConvert.SerializeObject( myList2)

But in general case you could use SequenceEqual as was mentioned in comments https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.sequenceequal?view=netframework-4.8

Also do not forget to implement IEquatable interface for custom types (not necessary for strings type or other structure)

Oleg Bondarenko
  • 1,694
  • 1
  • 16
  • 19