Commit 407c7343 authored by Raymond Hettinger's avatar Raymond Hettinger Committed by GitHub

bpo-36057 Update docs and tests for ordering in collections.Counter [no behavior change] (#11962)

* Add tests for Counter order.  No behavior change.

* Update docs and tests

* Fix doctest output and capitalization
parent 86f093f7
......@@ -268,6 +268,11 @@ For example::
.. versionadded:: 3.1
.. versionchanged:: 3.7 As a :class:`dict` subclass, :class:`Counter`
Inherited the capability to remember insertion order. Math operations
on *Counter* objects also preserve order. Results are ordered
according to when an element is first encountered in the left operand
and then by the order encountered in the right operand.
Counter objects support three methods beyond those available for all
dictionaries:
......@@ -275,8 +280,8 @@ For example::
.. method:: elements()
Return an iterator over elements repeating each as many times as its
count. Elements are returned in arbitrary order. If an element's count
is less than one, :meth:`elements` will ignore it.
count. Elements are returned in the order first encountered. If an
element's count is less than one, :meth:`elements` will ignore it.
>>> c = Counter(a=4, b=2, c=0, d=-2)
>>> sorted(c.elements())
......@@ -287,10 +292,10 @@ For example::
Return a list of the *n* most common elements and their counts from the
most common to the least. If *n* is omitted or ``None``,
:meth:`most_common` returns *all* elements in the counter.
Elements with equal counts are ordered arbitrarily:
Elements with equal counts are ordered in the order first encountered:
>>> Counter('abracadabra').most_common(3) # doctest: +SKIP
[('a', 5), ('r', 2), ('b', 2)]
>>> Counter('abracadabra').most_common(3)
[('a', 5), ('b', 2), ('r', 2)]
.. method:: subtract([iterable-or-mapping])
......
......@@ -570,8 +570,8 @@ class Counter(dict):
'''List the n most common elements and their counts from the most
common to the least. If n is None, then list all element counts.
>>> Counter('abcdeabcdabcaba').most_common(3)
[('a', 5), ('b', 4), ('c', 3)]
>>> Counter('abracadabra').most_common(3)
[('a', 5), ('b', 2), ('r', 2)]
'''
# Emulate Bag.sortedByCount from Smalltalk
......
......@@ -1881,6 +1881,63 @@ class TestCounter(unittest.TestCase):
self.assertRaises(TypeError, Counter, (), ())
self.assertRaises(TypeError, Counter.__init__)
def test_order_preservation(self):
# Input order dictates items() order
self.assertEqual(list(Counter('abracadabra').items()),
[('a', 5), ('b', 2), ('r', 2), ('c', 1), ('d', 1)])
# letters with same count: ^----------^ ^---------^
# Verify retention of order even when all counts are equal
self.assertEqual(list(Counter('xyzpdqqdpzyx').items()),
[('x', 2), ('y', 2), ('z', 2), ('p', 2), ('d', 2), ('q', 2)])
# Input order dictates elements() order
self.assertEqual(list(Counter('abracadabra simsalabim').elements()),
['a', 'a', 'a', 'a', 'a', 'a', 'a', 'b', 'b', 'b','r',
'r', 'c', 'd', ' ', 's', 's', 'i', 'i', 'm', 'm', 'l'])
# Math operations order first by the order encountered in the left
# operand and then by the order encounted in the right operand.
ps = 'aaabbcdddeefggghhijjjkkl'
qs = 'abbcccdeefffhkkllllmmnno'
order = {letter: i for i, letter in enumerate(dict.fromkeys(ps + qs))}
def correctly_ordered(seq):
'Return true if the letters occur in the expected order'
positions = [order[letter] for letter in seq]
return positions == sorted(positions)
p, q = Counter(ps), Counter(qs)
self.assertTrue(correctly_ordered(+p))
self.assertTrue(correctly_ordered(-p))
self.assertTrue(correctly_ordered(p + q))
self.assertTrue(correctly_ordered(p - q))
self.assertTrue(correctly_ordered(p | q))
self.assertTrue(correctly_ordered(p & q))
p, q = Counter(ps), Counter(qs)
p += q
self.assertTrue(correctly_ordered(p))
p, q = Counter(ps), Counter(qs)
p -= q
self.assertTrue(correctly_ordered(p))
p, q = Counter(ps), Counter(qs)
p |= q
self.assertTrue(correctly_ordered(p))
p, q = Counter(ps), Counter(qs)
p &= q
self.assertTrue(correctly_ordered(p))
p, q = Counter(ps), Counter(qs)
p.update(q)
self.assertTrue(correctly_ordered(p))
p, q = Counter(ps), Counter(qs)
p.subtract(q)
self.assertTrue(correctly_ordered(p))
def test_update(self):
c = Counter()
c.update(self=42)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment