mercurial: implement diff and join for dicts
authorSiddharth Agarwal <sid0@fb.com>
Mon, 25 Mar 2013 17:40:39 -0700
changeset 18820 a45e44d76c81
parent 18819 05acdf8e1f23
child 18821 40b4b1f9b7a0
mercurial: implement diff and join for dicts Given two dicts, diff returns a dict containing all the keys that are present in one dict but not the other, or whose values are different between the dicts. The values are pairs of the values from the dicts, with missing values being represented as an optional argument, defaulting to None. Given two dicts, join performs what is known as an outer join in relational database land: it returns a dict containing all the keys across both dicts. The values are pairs as above, except they aren't compared to see if they're the same.
mercurial/dicthelpers.py
tests/test-dicthelpers.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial/dicthelpers.py	Mon Mar 25 17:40:39 2013 -0700
@@ -0,0 +1,35 @@
+# dicthelpers.py - helper routines for Python dicts
+#
+# Copyright 2013 Facebook
+#
+# This software may be used and distributed according to the terms of the
+# GNU General Public License version 2 or any later version.
+
+def _diffjoin(d1, d2, default, compare):
+    res = {}
+    if d1 is d2 and compare:
+        # same dict, so diff is empty
+        return res
+
+    for k1, v1 in d1.iteritems():
+        if k1 in d2:
+            v2 = d2[k1]
+            if not compare or v1 != v2:
+                res[k1] = (v1, v2)
+        else:
+            res[k1] = (v1, default)
+
+    if d1 is d2:
+        return res
+
+    for k2 in d2:
+        if k2 not in d1:
+            res[k2] = (default, d2[k2])
+
+    return res
+
+def diff(d1, d2, default=None):
+    return _diffjoin(d1, d2, default, True)
+
+def join(d1, d2, default=None):
+    return _diffjoin(d1, d2, default, False)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-dicthelpers.py	Mon Mar 25 17:40:39 2013 -0700
@@ -0,0 +1,53 @@
+from mercurial.dicthelpers import diff, join
+import unittest
+import silenttestrunner
+
+class testdicthelpers(unittest.TestCase):
+    def test_dicthelpers(self):
+        # empty dicts
+        self.assertEqual(diff({}, {}), {})
+        self.assertEqual(join({}, {}), {})
+
+        d1 = {}
+        d1['a'] = 'foo'
+        d1['b'] = 'bar'
+        d1['c'] = 'baz'
+
+        # same identity
+        self.assertEqual(diff(d1, d1), {})
+        self.assertEqual(join(d1, d1), {'a': ('foo', 'foo'),
+                                        'b': ('bar', 'bar'),
+                                        'c': ('baz', 'baz')})
+
+        # vs empty
+        self.assertEqual(diff(d1, {}), {'a': ('foo', None),
+                                        'b': ('bar', None),
+                                        'c': ('baz', None)})
+        self.assertEqual(diff(d1, {}), {'a': ('foo', None),
+                                        'b': ('bar', None),
+                                        'c': ('baz', None)})
+
+        d2 = {}
+        d2['a'] = 'foo2'
+        d2['b'] = 'bar'
+        d2['d'] = 'quux'
+
+        self.assertEqual(diff(d1, d2), {'a': ('foo', 'foo2'),
+                                        'c': ('baz', None),
+                                        'd': (None, 'quux')})
+        self.assertEqual(join(d1, d2), {'a': ('foo', 'foo2'),
+                                        'b': ('bar', 'bar'),
+                                        'c': ('baz', None),
+                                        'd': (None, 'quux')})
+
+        # with default argument
+        self.assertEqual(diff(d1, d2, 123), {'a': ('foo', 'foo2'),
+                                             'c': ('baz', 123),
+                                             'd': (123, 'quux')})
+        self.assertEqual(join(d1, d2, 456), {'a': ('foo', 'foo2'),
+                                             'b': ('bar', 'bar'),
+                                             'c': ('baz', 456),
+                                             'd': (456, 'quux')})
+
+if __name__ == '__main__':
+    silenttestrunner.main(__name__)