247

When writing test cases, I often need to assert that two list contain the same elements without regard to their order.

I have been doing this by converting the lists to sets.

Is there any simpler way to do this?

EDIT:

As @MarkDickinson pointed out, I can just use TestCase.assertItemsEqual.

Notes that TestCase.assertItemsEqual is new in Python2.7. If you are using an older version of Python, you can use unittest2 - a backport of new features of Python 2.7.

satoru
  • 31,822
  • 31
  • 91
  • 141
  • 6
    Simpler than `set(x) == set(y)`? How much simpler can you get? – cdhowie Oct 10 '12 at 06:58
  • 12
    @cdhowie: This will fail when there are redundant elements in the lists. – inspectorG4dget Oct 10 '12 at 07:00
  • @cdhowie What about extending `TestCase` and add a `assertSetEqual`? IMHO, calling `self.assertSetEqual` is simpler than remembering to use `set(x) == set(y)` anywhere. – satoru Oct 10 '12 at 07:01
  • 2
    @inspectorG4dget It's not clear from the original question whether that should be a failure case or not. – cdhowie Oct 10 '12 at 07:01
  • 27
    If you're unit testing, what's wrong with `TestCase.assertItemsEqual`? – Mark Dickinson Oct 10 '12 at 07:22
  • 2
    @MarkDickinson "without regard to their order". – glglgl Oct 10 '12 at 07:54
  • @MarkDickinson I didn't know that before. It's really nice to have, but I'm using Python2.6 and `assertItemsEqual` is new in version2.7. – satoru Oct 10 '12 at 08:21
  • Ah yes, Python 2.6 would be an excellent reason *not* to use `assertItemsEqual`. :-) – Mark Dickinson Oct 10 '12 at 13:10
  • @MarkDickinson You should make that an answer so OP can accept it and we can upvote it - that really is the best answer considering the question states "writing test cases". This is a top 2 result when Googling "assert lists equal python". – Jmills Jun 17 '15 at 18:36
  • [assertSetEqual](https://docs.python.org/3/library/unittest.html#unittest.TestCase.assertSetEqual) is available for python 3.1. referring to @satoru comment – Abhijeet Mar 25 '21 at 10:14

5 Answers5

261

As of Python 3.2 unittest.TestCase.assertItemsEqual(doc) has been replaced by unittest.TestCase.assertCountEqual(doc) which does exactly what you are looking for, as you can read from the python standard library documentation. The method is somewhat misleadingly named but it does exactly what you are looking for.

a and b have the same elements in the same number, regardless of their order

Here a simple example which compares two lists having the same elements but in a different order.

  • using assertCountEqual the test will succeed
  • using assertListEqual the test will fail due to the order difference of the two lists

Here a little example script.

import unittest


class TestListElements(unittest.TestCase):
    def setUp(self):
        self.expected = ['foo', 'bar', 'baz']
        self.result = ['baz', 'foo', 'bar']

    def test_count_eq(self):
        """Will succeed"""
        self.assertCountEqual(self.result, self.expected)

    def test_list_eq(self):
        """Will fail"""
        self.assertListEqual(self.result, self.expected)

if __name__ == "__main__":
    unittest.main()

Side Note : Please make sure that the elements in the lists you are comparing are sortable.

kmad1729
  • 1,484
  • 1
  • 16
  • 20
flazzarini
  • 7,791
  • 5
  • 33
  • 34
  • 26
    Thanks. The original name was so much clearer, I had seen that method, but assumed it checked the count. – ChrisProsser Sep 30 '16 at 16:58
  • Is there a way to compare to lists that are "almost equal"? In the sense that given an error, i want to assert the equal if the difference btw the elements in the lists are within the error? – JustANoob Sep 16 '18 at 10:49
  • 6
    Thanks. But *downright* misleading, I would have called it! – Wild Pottok Oct 22 '18 at 16:18
  • This is not correct - try self.assertCountEqual({1: [1, 2, 3]}, {1: [5, 6, 7]}) – variable Nov 18 '19 at 08:05
  • Note - in Python 2 you can use the six library to do six.assertCountEqual, for any future py3 upgrade :) – Arya Nov 27 '19 at 23:56
  • Is there a way to see if everything in list B is in list A. But not everything in list A has to be in list B. When list B is bigger than list A and may have (more than likely) duplicates in it. So I have some predictions and I want to make sure that every prediction belongs to list A – Callum Smyth Jun 30 '20 at 16:38
  • Is there a way to check the opposite, that is, whether to lists are NOT equal? – Daniel Jul 02 '20 at 17:21
  • Unfortunately not, the way `assertCountEqual` is implemented it will always generate a `fail()` if something is different. I couldn't find a way to inverse the behavior. – flazzarini Jul 09 '20 at 12:13
62

Slightly faster version of the implementation (If you know that most couples lists will have different lengths):

def checkEqual(L1, L2):
    return len(L1) == len(L2) and sorted(L1) == sorted(L2)

Comparing:

>>> timeit(lambda: sorting([1,2,3], [3,2,1]))
2.42745304107666
>>> timeit(lambda: lensorting([1,2,3], [3,2,1]))
2.5644469261169434 # speed down not much (for large lists the difference tends to 0)

>>> timeit(lambda: sorting([1,2,3], [3,2,1,0]))
2.4570400714874268
>>> timeit(lambda: lensorting([1,2,3], [3,2,1,0]))
0.9596951007843018 # speed up
defuz
  • 26,721
  • 10
  • 38
  • 60
  • 2
    Your speedup comes from (1) no branching (valid), (2) a `len` check before sorting. The `len` check is O(n) and only helps in cases when it returns `False`. Else, it hurts the actual runtime (not complexity) by adding two linear passes (one on each of `L1` and `L2`). Thus while the runtime complexity is still O(nlogn) (from sorting), the O(n) will hurt the number of seconds it takes for this function to `return` – inspectorG4dget Oct 10 '12 at 07:16
  • @inspectorG4dget, add comparison (without branching difference) – defuz Oct 10 '12 at 07:25
  • 1
    This is exactly what I meant. Yours is actually a better solution if OP knows a priori that most of the pairs of lists that he compares will be of unequal length – inspectorG4dget Oct 10 '12 at 07:27
  • 18
    @inspectorG4dget: `len()` has O(1) complexity (constant time). There is no pass to get the value. – pepr Oct 10 '12 at 07:30
  • @pepr: [you're right](http://stackoverflow.com/questions/1115313/cost-of-len-function)! I was not aware of that. Thank you for clarifying – inspectorG4dget Oct 10 '12 at 07:32
  • 2
    This doesn’t work if your list contains unorderable types like `dict`. – bfontaine Jul 04 '16 at 17:01
  • the marked duplicated post wasn't asking for unit testing and further more that question wasn't stated clear enough. so I don't think this question should be marked as duplicate as the other one. – user2617470 Aug 14 '19 at 12:15
  • This is not correct! Try self.assertCountEqual({1: [1, 2, 3]}, {1: [5, 6, 7]}) – variable Nov 18 '19 at 08:05
  • @variable None of the functions in this thread are intended to be called with dicts. The question asks "How to assert two **list** contain the same elements in Python?" (emphasis mine). Recursively checking list order in a nested structure is a completely separate issue. – ggorlen Aug 08 '23 at 19:51
  • Sorting is slow O(n log(n)), so this answer is misleading. If performance matters, use a lookup table (dictionary or `Collections.counter`) O(n). That said, in most cases you wouldn't want to use large lists in tests anyway so it probably doesn't matter. – ggorlen Aug 08 '23 at 19:52
40

Given

l1 = [a,b]
l2 = [b,a]

In Python >= 3.0

assertCountEqual(l1, l2) # True

In Python >= 2.7, the above function was named:

assertItemsEqual(l1, l2) # True

In Python < 2.7

import unittest2
assertItemsEqual(l1, l2) # True

Via six module (Any Python version)

import unittest
import six
class MyTest(unittest.TestCase):
    def test(self):
        six.assertCountEqual(self, self.l1, self.l2) # True
Cory Klein
  • 51,188
  • 43
  • 183
  • 243
  • 2
    **If there are only unique values in list** Converting them to set using set(l1)& set(2) & asserting using assertSetEqual(l1, l2) also helps. – Abhijeet Jun 25 '18 at 08:34
  • +1 for your solution @Abhijeet as it actually checks if the contents of the list matches, which is useful for comparing a set of unique ids. – Sameen Aug 16 '19 at 09:44
  • Try self.assertCountEqual({1: [1, 2, 3]}, {1: [5, 6, 7]}) – variable Nov 18 '19 at 08:06
20

Converting your lists to sets will tell you that they contain the same elements. But this method cannot confirm that they contain the same number of all elements. For example, your method will fail in this case:

L1 = [1,2,2,3]
L2 = [1,2,3,3]

You are likely better off sorting the two lists and comparing them:

def checkEqual(L1, L2):
    if sorted(L1) == sorted(L2):
        print "the two lists are the same"
        return True
    else:
        print "the two lists are not the same"
        return False

Note that this does not alter the structure/contents of the two lists. Rather, the sorting creates two new lists

inspectorG4dget
  • 110,290
  • 27
  • 149
  • 241
1

Needs ensure library but you can compare list by:

ensure([1, 2]).contains_only([2, 1])

This will not raise assert exception. Documentation of thin is really thin so i would recommend to look at ensure's codes on github

radeklos
  • 2,168
  • 21
  • 19