2016-12-05 35 views
1

我想在字典中识别并分组重复值。要做到这一点,我建立一个伪哈希(更好地阅读签名)我的数据集如下的:在字典中查找混合类型值的重复项

from pickle import dumps 
taxonomy = {} 
binder = defaultdict(list) 
for key, value in ds.items(): 
    signature = dumps(value) 
    taxonomy[signature] = value 
    binder[signature].append(key) 

对于具体使用情况请参阅本question

不幸的是,我意识到,如果下面的语句是True

>>> ds['key1'] == ds['key2'] 
True 

这个人是不是总是True了:

>>> dumps(ds['key1']) == dumps(ds['key2']) 
False 

我注意到在倾倒输出键顺序两个字典不同。如果我拷贝/粘贴ds['key1']ds['key2']的输出成新词典我可以比较成功。

作为一个矫枉过正的替代,我可以递归遍历我的数据集,并与OrderedDict替换dict实例:

import copy 
def faithfulrepr(od): 
    od = od.deepcopy(od) 
    if isinstance(od, collections.Mapping): 
     res = collections.OrderedDict() 
     for k, v in sorted(od.items()): 
      res[k] = faithfulrepr(v) 
     return repr(res) 
    if isinstance(od, list): 
     for i, v in enumerate(od): 
      od[i] = faithfulrepr(v) 
     return repr(od) 
    return repr(od) 

>>> faithfulrepr(ds['key1']) == faithfulrepr(ds['key2']) 
True 

我很担心这种幼稚的做法,因为我不知道我是否涵盖所有可能发生的情况。

可以使用哪些其他(通用)替代方案?

+0

所以,你希望每个'dict'的'repr'是相同的......任何原因不能只是'加载'他们和比较? –

+0

好的,使用'repr'可以比使用'pickle'容易得多。不幸的是,'repr'对象并不总是足以比较两个实例的内容。这就是说'repr(ds ['key1'])== repr(ds ['key2'])'也会返回False ... – nowox

+0

我的意思是 - 你需要* dict'对象的*表示*比较相等 - 无论是JSON /酸洗/'str''d /'repr''d等等......为什么不反过来再比较一下呢?例如:'loads(dumps(ds ['key1']))== loads(dumps(ds ['key2']))'? –

回答

1

的第一件事就是删除对deepcopy调用哪个是你的瓶颈在这里:

def faithfulrepr(ds): 
    if isinstance(ds, collections.Mapping): 
     res = collections.OrderedDict(
      (k, faithfulrepr(v)) for k, v in sorted(ds.items()) 
     ) 
    elif isinstance(ds, list): 
     res = [faithfulrepr(v) for v in ds] 
    else: 
     res = ds 
    return repr(res) 

然而sortedrepr有其缺点:

  1. 你不能真实地比较的自定义类型;
  2. 你不能使用不同类型的键的映射。

所以第二件事是要摆脱faithfulrepr和比较对象与__eq__

binder, values = [], [] 
for key, value in ds.items(): 
    try: 
     index = values.index(value) 
    except ValueError: 
     values.append(value) 
     binder.append([key]) 
    else: 
     binder[index].append(key) 
grouped = dict(zip(map(tuple, binder), values))