31

I have two lists of integers, list1 and list2. The elements in the lists are the same, but the order is not important. How can I determine whether these lists are equal, ignoring the order of the elements? Is there a built-in method in C# that can do this, or do I need to write my own method?

var list1 = new List<int> {1,2,3};
var list2 = new List<int> {2,1,3};
Vahid
  • 5,144
  • 13
  • 70
  • 146
  • 1
    I don't think there's anything built in... good candidate for an extension method – Liath Jun 23 '14 at 08:04
  • 1
    Does this answer your question? [Quickest way to compare two generic lists for differences](https://stackoverflow.com/questions/12795882/quickest-way-to-compare-two-generic-lists-for-differences) – StayOnTarget Jan 21 '22 at 18:14

6 Answers6

43

That's what sets (e.g., HashSet<T>) are for. Sets have no defined order, and SetEquals verifies whether the set and another collection contain the same elements.

var set = new HashSet<int>(list1);
var equals = set.SetEquals(list2);
ab_732
  • 3,639
  • 6
  • 45
  • 61
dcastro
  • 66,540
  • 21
  • 145
  • 155
  • 1
    Would this work if I have a list of objects instead of integers? – Vahid Jun 23 '14 at 08:05
  • @Vahid Yeah it would. – dcastro Jun 23 '14 at 08:06
  • 1
    @Vahid: it depends on if the object overrides `Equals` (+ `GetHashCode`). You can also pass a custom `IEqualityComparer` to the constructor of `HashSet` Read: http://stackoverflow.com/questions/8952003/how-does-hashset-compare-elements-for-equality – Tim Schmelter Jun 23 '14 at 08:20
  • 10
    Be aware that this algorithm declares sequence {1, 1, 1, 1, 1, 1, 1} equal to {1}. Is this what you want? – Harald Coppoolse Nov 15 '17 at 10:26
  • 7
    There is a MAJOR difference between a set and a list. A set does have only unique elements. This is a wrong answer to the question. – mimarcel Jan 10 '19 at 15:52
  • It's old, but if you implement a count camparer by the logic i assume you should actually get the correct answer despite reocurring elements, therefore extension like below should work: `public static bool CompareList (this List _initialList, List _secondaryList) { var set = new HashSet(_initialList); var equals = set.SetEquals(_secondaryList); return equals ? _initialList.Count() == _secondaryList.Count() : false; }` –  May 18 '22 at 04:51
20

You can use !Except + Any:

bool list1InList2 = !list1.Except(list2).Any();

This checks not if both have the same items but if list1 is contained in list2(ignoring duplicates).

If you want to know if list2 is contained in list1, use:

bool list2InList1 = !list2.Except(list1).Any();

So you had to make both checks if you wanted to ensure that both lists contain the same items.

If you also want to take into account that both lists have the same size, precheck with list1.Count==list2.Count. But this check is not useful if you use a set method(see Harald's comment), it doesn't make much sense to compare the counts if you ignore duplicates afterwards.

In general HashSet<T> has some nice and efficient methods to check if two sequences have the same items(ignoring duplicates), dcastro already showed one.


If you want an efficient solution to determine if two lists contain the same items, same count and not ignoring duplicates but ignoring the order(otherwise use SequenceEquals):

public static bool SequenceEqualsIgnoreOrder<T>(this IEnumerable<T> list1, IEnumerable<T> list2, IEqualityComparer<T> comparer = null)
{
    if(list1 is ICollection<T> ilist1 && list2 is ICollection<T> ilist2 && ilist1.Count != ilist2.Count)
        return false;

    if (comparer == null)
        comparer = EqualityComparer<T>.Default;

    var itemCounts = new Dictionary<T, int>(comparer);
    foreach (T s in list1)
    {
        if (itemCounts.ContainsKey(s))
        {
            itemCounts[s]++;
        }
        else
        {
            itemCounts.Add(s, 1);
        }
    }
    foreach (T s in list2)
    {
        if (itemCounts.ContainsKey(s))
        {
            itemCounts[s]--;
        }
        else
        {
            return false;
        }
    }
    return itemCounts.Values.All(c => c == 0);
}

Usage:

var list1 = new List<int> { 1, 2, 3, 1 };
var list2 = new List<int> { 2, 1, 3, 2 };
bool sameItemsIgnoringOrder = list1.SequenceEqualsIgnoreOrder(list2); 
// false because same count and same items but 1 appaears twice in list1 but once in list2

If the order matters and duplicates count too, use:

bool sameItemsSameOrder = list1.SequenceEqual(list2);
Tim Schmelter
  • 450,073
  • 74
  • 686
  • 939
  • @Rotem I wonder if this is faster than the method suggested by `dcastro`? – Vahid Jun 23 '14 at 08:11
  • Thanks Tim, you mean `dcastro`'s method will ignore duplicates? – Vahid Jun 23 '14 at 08:13
  • Thank you both for the clarification. I think I need to adapt this to my situation first to decide which one I need. – Vahid Jun 23 '14 at 08:18
  • @HaraldCoppoolse: you are right, so i have edited my answer – Tim Schmelter Nov 15 '17 at 10:47
  • @HaraldCoppoolse: true, but as you have said, the `Count` check might be efficient but either the count doesnt matter(because duplicates are ignored anyway) or a non-set approach is needed anyway. I have removed my comments – Tim Schmelter Nov 15 '17 at 10:55
  • @dcastro: maybe you want to delete your comments regarding `Count` because i have edited my answer. I dont think the `Count` check is useful in this case – Tim Schmelter Nov 15 '17 at 10:59
  • @harald: added another extension method which you can use to check if two lists contain the same items but ignoring the order. – Tim Schmelter Nov 15 '17 at 11:21
  • 1
    **NOTE** this approach does NOT support `null` items since `Dictionary` doesn't. `itemCounts.Add(s,1)` throws `ArgumentNullException` when `s` is `null`. – N. Kudryavtsev Dec 05 '18 at 15:18
  • 1
    @N.Kudryavtsev: Good point. Maybe you could use this class as key: https://stackoverflow.com/a/22261282/284240 – Tim Schmelter Dec 05 '18 at 15:52
2

It's old but i would modify answer by dcastro, and build in extension:

public static bool CompareList(this List<string> _initialList, List<string> _secondaryList) 
{ 
    var set = new HashSet<string>(_initialList); 
    var equals = set.SetEquals(_secondaryList); 
    return equals ? _initialList.Count() == _secondaryList.Count() : false; 
}

This way You would check both elements within the list with sets, but also count of elements helps you to check against excample given in coments like {0, 1, 1} vs {0 ,1}

Additionaly by extension you can call it multiple times within the code. You could modify it to compare against potential null.

public static bool CompareList(this List<string> _initialList, List<string>? _secondaryList) 
{
    //test if _secondaryList is null while initial is not
    if (_secondaryList == null) return false;

    var set = new HashSet<string>(_initialList); 
    var equals = set.SetEquals(_secondaryList); 
    return equals ? _initialList.Count() == _secondaryList.Count() : false; 
}
1

Without using linq.

 private static bool AreListsEqual(List<int> list1, List<int> list2)
 {
        var areListsEqual = true;

        if (list1.Count != list2.Count)
            return false;

        for (var i = 0; i < list1.Count; i++)
        {
            if (list2[i] != list1[i])
            {
                areListsEqual = false;
            }
        }

        return areListsEqual;
 }
brumScouse
  • 3,166
  • 1
  • 24
  • 38
  • 3
    There order is not important but your solution returns false when the numbers are not in the same order. – Vahid Jun 23 '14 at 08:26
  • If you need to use a version as the above code, then you can actually use the core implemented `SequenceEquals` method. – mimarcel Jan 15 '19 at 11:03
1

Use SequenceEqual against the ordered lists - e.g.

bool equal = list1.OrderBy(x => x).ToList().SequenceEqual(list2.OrderBy(x => x).ToList());
Graham Laight
  • 4,700
  • 3
  • 29
  • 28
0

You can try Except

var result = list1.Except(list2).ToList();

Except returns those elements in first that do not appear in second

Kamlesh Arya
  • 4,864
  • 3
  • 21
  • 28
  • Why `string`? Also, where is the check for equality? – Rotem Jun 23 '14 at 08:07
  • 4
    If `list1` is empty this returns an empty list which does not mean that both are equal. It also fails if list1 is a subset of list2. Apart from that it is not necessary to create another collection just to check if both lists are equal. – Tim Schmelter Jun 23 '14 at 08:36