views:

406

answers:

7

I have two lists A and B (List). How to determine if they are equal in the cheapest way? I can write something like '(A minus B) union (B minus A) = empty set' or join them together and count amount of elements, but it is rather expensive. Is there workaround?

A: 

A first shot - if they contain the same items, the union of both list should have the same number of items as any of both lists.

listA.Union(listB).Count() == listA.Count()

Note: Fails if one list is empty.

But it's probably still a O(n²) operation.

Another solution - the lists must have the same length and list A minus list B must not contain any elements.

(listA.Count() == listB.Count()) && !listA.Except(listB).Any()
Daniel Brückner
Make it an Intersection - your version allows for more items in listA than in listB.
Ryan Versaw
if ListB is empty this falsely returns true.
Robert
Thanks for pointing that out - added a note.
Daniel Brückner
A: 

If you are working in c# 3.0 you can write an extension method that loops through all items in each list and compares them individually. Compare the lengths for equality first.

You can then call it like this:

bool result = listA.Equals(listB);
Robert Harvey
There is no such method out of the box, and there is a reason why :( It is not so simple.
Alsin
+1  A: 

It depends on what you mean by "the list are equal". If you mean they contain the same objects, the solution suggested by Daniel is fine, just Union() the two list and count the items.

If by "equal" you mean that they have the same items in the same order, you'd be better off comparing the Count of both lists, then if they have the same count, just iterate with a plain for loop to compare each element from both lists at the same index. Less pretty but you can hardly be faster.

Yann Schwartz
I mean equality of sets, are they contain same elements. Union() is still overkill.
Alsin
+1  A: 

There isn't a shortcut here, really, unless the lists are sorted, in which case you can compare the elements one-by-one. And obviously I assume that order doesn't matter, or else it's obvious that you can compare them one-by-one then too.

Otherwise, I'd suggest that the most efficient algorithm you could get for large lists of items would probably be something like this, using a hash table to keep track of what you've seen (warning: haven't tested, but it should be clear what I'm getting at.)

public static bool IsEqual<T>(this List<T> x1, List<T> x2)
{
    if(x1.Count != x2.Count) return false;

    var x1Elements = new Dictionary<T, int>();

    foreach(var item in x1)
    {
        int n; x1Elements.TryGetValue(item, out n);
        x1Elements[item] = n+1;
    }

    foreach(var item in x2)
    {
        int n; x1Elements.TryGetValue(item, out n);
        if(n <= 0) return false; // this element was in x2 but not x1
        else x1Elements[item] = n-1;
    }

    // make sure x1 didn't have any elements
    // that weren't in x2

    return x1Elements.Values.All(x => x == 0);
}
mquander
Yes, I'm using something like this at the moment.
Alsin
+1  A: 

Well, that depends on how you interpret your lists.

If you consider them as tuples (so the order of elements in lists matters), then you can go with this code:

    public bool AreEqual<T>(IList<T> A, IList<T> B)
    {
        if (A.Count != B.Count)
            return false;
        for (int i = 0; i < A.Count; i++)
            if (!A[i].Equals(B[i])) 
                return false;
    }

If you consider your lists as sets (so the order of elements doesn't matter), then... you are using the wrong data structures I guess:

    public bool AreEqual<T>(IList<T> A, IList<T> B)
    {
        HashSet<T> setA = new HashSet<T>(A);
        return setA.SetEquals(B);
    }
Yacoder
Exactly. Data structure is wrong. I'll try to push HashSet.
Alsin
If you're gonna switch to HashSets ensure you have GetHashCode() functions working properly on your set objects.
Yacoder
A: 

Alsin, try this:

Comparing two arrays (or IEnumerables) in C#
http://blog.slaven.net.au/archives/2008/03/16/comparing-two-arrays-or-ienumerables-in-c/

Robert Harvey
+1  A: 

If the ordering of the list items is relevant:

bool areEqual = a.SequenceEqual(b);

If the lists are to be treated as unordered sets:

// assumes that the list items are ints
bool areEqual = new HashSet<int>(a).SetEquals(b);

(The SequenceEqual method and the HashSet constructor both have overloads that take an IEqualityComparer parameter, if you need that functionality.)

LukeH