【问题标题】:Find duplicates for mixed type values in dictionaries在字典中查找混合类型值的重复项
【发布时间】:2024-01-22 02:48:01
【问题描述】:

我想在字典中识别和分组重复值。为此,我为我的数据集构建了一个伪散列(更好地阅读签名),如下所示:

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

我注意到两个 dict.dump 输出的键顺序不同。如果我将ds['key1']ds['key2'] 的输出复制/粘贴到新字典中,我可以使比较成功。

作为一种矫枉过正的替代方案,我可以递归地遍历我的数据集并将dict 实例替换为OrderedDict

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

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

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

【问题讨论】:

  • 所以你希望每个dictrepr 相同...有什么理由不能只loads 他们比较?
  • 嗯,是的,使用repr 可能比使用pickle 容易得多。不幸的是,repr 对象并不总是足以比较两个实例的内容。也就是说repr(ds['key1']) == repr(ds['key2']) 也返回False...
  • 我的意思是 - 你希望 dict 对象的一些 表示 比较相等 - 无论是 JSON/pickled/str'd/repr'd等等...为什么不直接反转并再次比较?例如:loads(dumps(ds['key1'])) == loads(dumps(ds['key2']))?
  • 如果你看看我在binder[signature].append(key) 附近的例子,我并没有真正比较像a == b 这样的字典。所以你的解决方案在我的例子中不起作用。

标签: python dictionary hash pickle


【解决方案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))

【讨论】: