]> granicus.if.org Git - python/commitdiff
Add a subtract() method to collections.Counter()
authorRaymond Hettinger <python@rcn.com>
Sat, 3 Apr 2010 10:32:58 +0000 (10:32 +0000)
committerRaymond Hettinger <python@rcn.com>
Sat, 3 Apr 2010 10:32:58 +0000 (10:32 +0000)
Doc/library/collections.rst
Lib/collections.py
Lib/test/test_collections.py
Misc/NEWS

index 176975b238069f5c8df7b06df672203f2e62a1ce..aa17a559da47e2c46794dec258ba5f292014e618 100644 (file)
@@ -214,6 +214,17 @@ For example::
             >>> Counter('abracadabra').most_common(3)
             [('a', 5), ('r', 2), ('b', 2)]
 
+   .. method:: subtract([iterable-or-mapping])
+
+      Elements are subtracted from an *iterable* or from another *mapping*
+      (or counter).  Like :meth:`dict.update` but subtracts counts instead
+      of replacing them.  Both inputs and outputs may be zero or negative.
+
+            >>> c = Counter(a=4, b=2, c=0, d=-2)
+            >>> d = Counter(a=1, b=2, c=3, d=4)
+            >>> c.subtract(d)
+            Counter({'a': 3, 'b': 0, 'c': -3, 'd': -6})
+
    The usual dictionary methods are available for :class:`Counter` objects
    except for two which work differently for counters.
 
index 6ec062f9a458c4d925cc20c02188e6dbdeac5108..53e78f94d34773f67c5d63ee4561682037e079af 100644 (file)
@@ -436,6 +436,34 @@ class Counter(dict):
         if kwds:
             self.update(kwds)
 
+    def subtract(self, iterable=None, **kwds):
+        '''Like dict.update() but subtracts counts instead of replacing them.
+        Counts can be reduced below zero.  Both the inputs and outputs are
+        allowed to contain zero and negative counts.
+
+        Source can be an iterable, a dictionary, or another Counter instance.
+
+        >>> c = Counter('which')
+        >>> c.subtract('witch')             # subtract elements from another iterable
+        >>> c.subtract(Counter('watch'))    # subtract elements from another counter
+        >>> c['h']                          # 2 in which, minus 1 in witch, minus 1 in watch
+        0
+        >>> c['w']                          # 1 in which, minus 1 in witch, minus 1 in watch
+        -1
+
+        '''
+        if iterable is not None:
+            if isinstance(iterable, Mapping):
+                self_get = self.get
+                for elem, count in iterable.items():
+                    self[elem] = self_get(elem, 0) - count
+            else:
+                self_get = self.get
+                for elem in iterable:
+                    self[elem] = self_get(elem, 0) - 1
+        if kwds:
+            self.subtract(kwds)
+
     def copy(self):
         'Like dict.copy() but returns a Counter instance instead of a dict.'
         return Counter(self)
index 797b3508e005557a4df2636ddc1cedb5f23a6169..1bd49d95b2e0f42afb281872ba2139ec657426c6 100644 (file)
@@ -661,6 +661,16 @@ class TestCounter(unittest.TestCase):
                 set_result = setop(set(p.elements()), set(q.elements()))
                 self.assertEqual(counter_result, dict.fromkeys(set_result, 1))
 
+    def test_subtract(self):
+        c = Counter(a=-5, b=0, c=5, d=10, e=15,g=40)
+        c.subtract(a=1, b=2, c=-3, d=10, e=20, f=30, h=-50)
+        self.assertEqual(c, Counter(a=-6, b=-2, c=8, d=0, e=-5, f=-30, g=40, h=50))
+        c = Counter(a=-5, b=0, c=5, d=10, e=15,g=40)
+        c.subtract(Counter(a=1, b=2, c=-3, d=10, e=20, f=30, h=-50))
+        self.assertEqual(c, Counter(a=-6, b=-2, c=8, d=0, e=-5, f=-30, g=40, h=50))
+        c = Counter('aaabbcd')
+        c.subtract('aaaabbcce')
+        self.assertEqual(c, Counter(a=-1, b=0, c=-1, d=1, e=-1))
 
 class TestOrderedDict(unittest.TestCase):
 
index 164655a8cfac11db671f343b2feeaf972b2ce88d..561f49246a62032eb9ffe47012beae8480a29610 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -301,6 +301,8 @@ C-API
 Library
 -------
 
+- Added a subtract() method to collections.Counter().
+
 - Issue #8233: When run as a script, py_compile.py optionally takes a single
   argument `-` which tells it to read files to compile from stdin.  Each line
   is read on demand and the named file is compiled immediately.  (Original