Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Compare two List<T> objects for equality, ignoring order [duplicate]

People also ask

How do you check if two lists are equal without orders?

Using Counter() , we usually are able to get frequency of each element in list, checking for it, for both the list, we can check if two lists are identical or not. But this method also ignores the ordering of the elements in the list and only takes into account the frequency of elements.

How do you compare two lists of objects?

Java equals() method of List interface compares the specified object with the list for equality. It overrides the equals() method of Object class. This method accepts an object to be compared for equality with the list. It returns true if the specified object is equal to the list, else returns false.

How do you compare two objects using the equal method?

If the two objects have the same values, equals() will return true . In the second comparison, equals() checks to see whether the passed object is null, or if it's typed as a different class. If it's a different class then the objects are not equal. Finally, equals() compares the objects' fields.

How do you compare two lists irrespective orders in Java?

We can use the logic below to compare the equality of two lists using the assertTrue and assertFalse methods. In this first test, the size of both lists is compared before we check if the elements in both lists are the same. As both of these conditions return true, our test will pass.


If you want them to be really equal (i.e. the same items and the same number of each item), I think that the simplest solution is to sort before comparing:

Enumerable.SequenceEqual(list1.OrderBy(t => t), list2.OrderBy(t => t))

Edit:

Here is a solution that performs a bit better (about ten times faster), and only requires IEquatable, not IComparable:

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

Edit 2:

To handle any data type as key (for example nullable types as Frank Tzanabetis pointed out), you can make a version that takes a comparer for the dictionary:

public static bool ScrambledEquals<T>(IEnumerable<T> list1, IEnumerable<T> list2, IEqualityComparer<T> comparer) {
  var cnt = new Dictionary<T, int>(comparer);
  ...

If you don't care about the number of occurrences, I would approach it like this. Using hash sets will give you better performance than simple iteration.

var set1 = new HashSet<MyType>(list1);
var set2 = new HashSet<MyType>(list2);
return set1.SetEquals(set2);

This will require that you have overridden .GetHashCode() and implemented IEquatable<MyType> on MyType.


As written, this question is ambigous. The statement:

... they both have the same elements, regardless of their position within the list. Each MyType object may appear multiple times on a list.

does not indicate whether you want to ensure that the two lists have the same set of objects or the same distinct set.

If you want to ensure to collections have exactly the same set of members regardless of order, you can use:

// lists should have same count of items, and set difference must be empty
var areEquivalent = (list1.Count == list2.Count) && !list1.Except(list2).Any();

If you want to ensure two collections have the same distinct set of members (where duplicates in either are ignored), you can use:

// check that [(A-B) Union (B-A)] is empty
var areEquivalent = !list1.Except(list2).Union( list2.Except(list1) ).Any();

Using the set operations (Intersect, Union, Except) is more efficient than using methods like Contains. In my opinion, it also better expresses the expectations of your query.

EDIT: Now that you've clarified your question, I can say that you want to use the first form - since duplicates matter. Here's a simple example to demonstrate that you get the result you want:

var a = new[] {1, 2, 3, 4, 4, 3, 1, 1, 2};
var b = new[] { 4, 3, 2, 3, 1, 1, 1, 4, 2 };

// result below should be true, since the two sets are equivalent...
var areEquivalent = (a.Count() == b.Count()) && !a.Except(b).Any(); 

In addition to Guffa's answer, you could use this variant to have a more shorthanded notation.

public static bool ScrambledEquals<T>(this IEnumerable<T> list1, IEnumerable<T> list2)
{
  var deletedItems = list1.Except(list2).Any();
  var newItems = list2.Except(list1).Any();
  return !newItems && !deletedItems;          
}