dict diff

2010-11-19 Thread Jin Yi
so i came up with a diff method to compare 2 dicts.  i found it pretty
useful so i thought i'd share it with everyone.  you can see the doctest
to check out suggested uses.  since we can't modify built-ins, i
demonstrated adding a diff method to OrderedDict to show how one could
add it to your own mapping objects.

the core logic is compatible with python2 and 3 (i've only tested using
2.6.5 and 3.1.2).  the doctest is suited for python3 though.


the interface is pretty straightforward.  it would be awesome to have
this sucker as a method on the builtin dict, but that'd take a pep, no?


-- 
Jin Yi
__author__ = 'razamatan_retral_net_ignore_com'

from collections import Mapping

def diffdict(left, right):
   ''' returns the deep diff of two dicts.  the return value the tuple
   (left_diff, right_diff)

   >>> diffdict(1, None)
   (1, None)
   >>> diffdict({1:2}, None)
   ({1: 2}, None)
   >>> diffdict({1:2}, {3:4})
   ({1: 2, 3: }, {1: , 3: 4})
   >>> from collections import OrderedDict
   >>> x = OrderedDict({1:2, 3:{4:{5:[6,7], 8:9}}})
   >>> diffdict(x, x)
   (None, None)
   >>> y = {1:2, 3:[4]}
   >>> diffdict(x, y)
   ({3: {4: {8: 9, 5: [6, 7]}}}, {3: [4]})
   >>> y = {1:2, 3:{4:{5:[6,7]}}}
   >>> diffdict(x, y)
   ({3: {4: {8: 9}}}, {3: {4: {8: }}})
   >>> y = {1:2, 3:{4:{5:{6:7}, 8:9}}}
   >>> diffdict(x, y)
   ({3: {4: {5: [6, 7]}}}, {3: {4: {5: {6: 7)
   >>> del y[3]
   >>> diffdict(x, y)
   ({3: {4: {8: 9, 5: [6, 7]}}}, {3: })
   >>> y = {1:2, 3:{4:{5:[6,10], 8:9}}}
   >>> diffdict(x, y)
   ({3: {4: {5: [6, 7]}}}, {3: {4: {5: [6, 10]}}})
   >>> y = {1:100, 3:{4:{5:[6,7], 8:9}}}
   >>> diffdict(x, y)
   ({1: 2}, {1: 100})
   >>> diffdict(y, x)
   ({1: 100}, {1: 2})
   >>> x.__class__.diff = diffdict
   >>> x.__class__.__xor__ = diffdict
   >>> x.diff(x)
   (None, None)
   >>> x ^ y
   ({1: 2}, {1: 100})
   '''
   # base case
   if not isinstance(left, Mapping) or not isinstance(right, Mapping):
  return (left, right)
   # key exclusivity
   left_diff = dict(i for i in left.items() if i[0] not in right)
   right_diff = dict(i for i in right.items() if i[0] not in left)
   right_diff.update((k, KeyError) for k in left_diff if k not in right_diff)
   left_diff.update((k, KeyError) for k in right_diff if k not in left_diff)
   # value differences
   for k in (k for k in left if k in right):
  if left[k] != right[k]:
 (ld, rd) = diffdict(left[k], right[k])
 left_diff[k] = ld or None
 right_diff[k] = rd or None

   left_diff = left_diff or None
   right_diff = right_diff or None
   return (left_diff, right_diff)
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: dict diff

2010-11-19 Thread Jin Yi
i don't think this piece of code is obscure.  i think the use case is there 
when you know that dicta != dictb, but you need to know where they're different.

i wouldn't really care to have it on the dict since it's useful as an unbound 
method anyway.

On Sat, Nov 20, 2010 at 01:11:53AM -0500, Steve Holden wrote:
> On 11/19/2010 8:58 PM, Jin Yi wrote:
> > so i came up with a diff method to compare 2 dicts.  i found it pretty
> > useful so i thought i'd share it with everyone.  you can see the doctest
> > to check out suggested uses.  since we can't modify built-ins, i
> > demonstrated adding a diff method to OrderedDict to show how one could
> > add it to your own mapping objects.
> > 
> > the core logic is compatible with python2 and 3 (i've only tested using
> > 2.6.5 and 3.1.2).  the doctest is suited for python3 though.
> > 
> > 
> > the interface is pretty straightforward.  it would be awesome to have
> > this sucker as a method on the builtin dict, but that'd take a pep, no?
> > 
> > 
> > 
> A PEP *and* some explanation of why you would want such an obscure piece
> of code built in to the dict object, yes.
> 
> regards
>  Steve
> -- 
> Steve Holden   +1 571 484 6266   +1 800 494 3119
> PyCon 2011 Atlanta March 9-17   http://us.pycon.org/
> See Python Video!   http://python.mirocommunity.org/
> Holden Web LLC http://www.holdenweb.com/
> 
> -- 
> http://mail.python.org/mailman/listinfo/python-list
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: dict diff

2010-11-20 Thread Jin Yi
right.  i moved to a 2-value tuple return value basically to illustrate exactly 
where each dict is providing a different value.  it turns out to be much easier 
to grok than the 3-value one since it's all there and lines up correctly with 
the inputs...

On Sat, Nov 20, 2010 at 12:00:16PM +, Steven D'Aprano wrote:
> On Sat, 20 Nov 2010 01:11:53 -0500, Steve Holden wrote:
> 
> > On 11/19/2010 8:58 PM, Jin Yi wrote:
> >> so i came up with a diff method to compare 2 dicts. 
> [...]
> > A PEP *and* some explanation of why you would want such an obscure piece
> > of code built in to the dict object, yes.
> 
> You've never wanted to see how two dicts differ, as well as the fact that 
> they do? I frequently find myself wanting to see why two dicts that 
> should be equal aren't, especially for testing and debugging.
> 
> I use this function:
> 
> def dict_diffs(a, b):
> """dict_diffs(adict, bdict) -> (amissing, bmissing, different)
> 
> Returns sets (amissing, bmissing, different) such that:
> 
> amissing = keys missing from adict compared to bdict
> bmissing = keys missing from bdict compared to adict
> different = keys in both adict and bdict but with different values
> 
> >>> dict_diffs({1:0, 2:0, 3:0}, {1:1, 2:0, 4:0})
> (set([4]), set([3]), set([1]))
> 
> """
> from collections import Mapping
> if not isinstance(a, Mapping) and isinstance(b, Mapping):
> raise TypeError('arguments must both be mappings')
> amissing, bmissing, different = set(), set(), set()
> for key, value in a.items():
> if key not in b:
> bmissing.add(key)
> elif value != b[key]:
> different.add(key)
> for key, value in b.items():
> if key not in a:
> amissing.add(key)
> return (amissing, bmissing, different)
> 
> 
> -- 
> Steven
> -- 
> http://mail.python.org/mailman/listinfo/python-list

-- 
I pops mah collah.  (Busta Rhymes)
Jin Yi -- http://www.retral.net/razamatan
-- 
http://mail.python.org/mailman/listinfo/python-list