比较三个(或更多)字典并在至少两个相等时找到匹配项

Posted

技术标签:

【中文标题】比较三个(或更多)字典并在至少两个相等时找到匹配项【英文标题】:Comparing three (or more) dictionaries and finding a match if at least two are equal 【发布时间】:2019-07-05 09:13:34 【问题描述】:

我遇到了类似于this one 的问题。但是,该 SO 问题严格集中在三个变量上。我正在寻找一种适用于三个以上的解决方案。

这是我的两个变量的代码:

for track_a in collection_a:
    for track_b in collection_b:

        t1 = track_a["tempo"]
        t2 = track_b["tempo"]
        k1 = track_a["key"]
        k2 = track_b["key"]
        m1 = track_a["mode"]
        m2 = track_b["mode"]

        if (t1 == t2) and (k1 == k2) and (m1 == m2):
            collection_c.append((track_a, track_b))

这是我对三个变量的解决方案:

for track_a in collection_a:
    for track_b in collection_b:
        for track_c in collection_c:

            t1 = track_a["tempo"]
            t2 = track_b["tempo"]
            t3 = track_c["tempo"]
            k1 = track_a["key"]
            k2 = track_b["key"]
            k3 = track_c["key"]
            m1 = track_a["mode"]
            m2 = track_b["mode"]
            m3 = track_c["mode"]

            a = (t1 == t2) and (k1 == k2) and (m1 == m2)
            b = (t2 == t3) and (k2 == k3) and (m2 == m3)
            c = (t3 == t1) and (k3 == k1) and (m3 == m1)

            if a: collection_c.append((track_a, track_b))
            if b: collection_c.append((track_b, track_c))
            if c: collection_c.append((track_c, track_a))

显然,此解决方案不可扩展且速度慢。考虑到我必须检查所有这些事实,我怀疑它会不会很快,因为我们必须迭代所有可能的组合,但我至少可以让它规模化吗? (最多至少 5 个)。此外,如果可能,允许稍后添加更多比较特征。

【问题讨论】:

您想将匹配项添加到完全不同的列表中吗?我的理解是? 我看不出这个问题与您链接的问题有何相似之处。您似乎想在 n 集合中匹配对,但我认为您的三重循环与为 (a, b)、然后 (b, c) 然后 (c) 运行双循环相同, a),因为您永远不会将这三个都匹配。 @aws_apprentice 是的。新列表是匹配对的组合。 【参考方案1】:

在线性时间内解决问题的一种有效方法是将字典转换为冻结的键值元组集(通过用于相等性测试的键),以便它们可以散列并用作字典键(签名)他们自己,这样你就可以简单地使用集合的字典来对它们进行分组:

groups = 
for track in collections: # collections is a combination of all the collections you have
    groups.setdefault(frozenset((k, track[k]) for k in ('tempo', 'key', 'mode')), set()).add(track['name'])

这样:

[group for group in groups.values() if len(group) >= 3]

将返回一组签名相同的 3 首曲目的名称列表。

【讨论】:

这简直就是魔法。是否有任何资源可以找到有关该主题的更多信息? (你为什么决定使用冻结集,这个想法是如何使用签名作为密钥的,等等)。我将此标记为正确答案,因为它简短、甜美、可扩展,并提供大量信息/理论。 很高兴能提供帮助。使用合理唯一的哈希键来表示复杂的数据结构是一种常见的做法,这样它们的内容就可以有效地相互比较。在 Python 中,dicts 和 sets 等可变对象是不可散列的,而 tuples 和 freeze set 等不可变对象是可散列的,因此解决方法是将 dicts 转换为键值对元组的冻结集,以便它们可以散列和使用作为字典键。【参考方案2】:

这是一个逻辑上可扩展的解决方案,对于在 m 值上比较 n 字典需要时间 n*m 来评估。

请注意,如果三个匹配,我将返回一组 3。很容易将其炸毁到所有匹配的对。但如果你这样做,那么你可能会返回大小为n*n 的东西。我已经向你展示了两者的样子。

def group_on(variables, *tracks):
    # Build a trie first.
    trie = 
    for track in tracks:
        this_path = trie
        for variable in variables:
            value = track[variable]
            if value not in this_path:
                this_path[value] = 
            this_path = this_path[value]
        if 'final' not in this_path:
            this_path['final'] = [track]
        else:
            this_path['final'].append(track)

    def find_groups(this_path, count):
        if 0 == count:
            if 1 < len(this_path['final']):
                yield this_path['final']
        else:
            for next_path in this_path.values():
                for group in find_groups(next_path, count-1):
                    yield group

    for group in find_groups(trie, len(variables)):
        yield group

def group_to_pairs(group):
    for i in range(len(group)-1):
        for j in range(i+1, len(group)):
            yield (group[i], group[j])

print('Efficient version')

for group in group_on(['tempo', 'key', 'mode'],
        'track': 1, 'tempo': 1, 'key': 'A', 'mode': 'minor',
        'track': 2, 'tempo': 1, 'key': 'A', 'mode': 'major',
        'track': 3, 'tempo': 1, 'key': 'A', 'mode': 'minor',
        'track': 4, 'tempo': 1, 'key': 'A', 'mode': 'major',
        'track': 5, 'tempo': 1, 'key': 'A', 'mode': 'minor',
        ):
    print(group)

print('Versus')

for group in group_on(['tempo', 'key', 'mode'],
        'track': 1, 'tempo': 1, 'key': 'A', 'mode': 'minor',
        'track': 2, 'tempo': 1, 'key': 'A', 'mode': 'major',
        'track': 3, 'tempo': 1, 'key': 'A', 'mode': 'minor',
        'track': 4, 'tempo': 1, 'key': 'A', 'mode': 'major',
        'track': 5, 'tempo': 1, 'key': 'A', 'mode': 'minor',
        ):
    for pair in group_to_pairs(group):
        print(pair)

【讨论】:

【参考方案3】:

itertools 中找到有用的东西,不确定这是不是你想要的:

from itertools import product, combinations

all_collections = [collection_a, collection_b, collection_c] # d, e, f, ...
for collections in combinations(all_collections, 2):         # Pick 2 (or any number) collections from all collections
    for tracks in product(*collections):                     # Cartesian product of collections or equivalent to for track1 in collection1: for track2 in collection2: ...
        if True:                                             # check if all tracks are matched
            print(*tracks)                                   # or append them to another collection

【讨论】:

以上是关于比较三个(或更多)字典并在至少两个相等时找到匹配项的主要内容,如果未能解决你的问题,请参考以下文章

在Java中检查三个或更多字符串的相等性[重复]

切片字典的值

js ==与===区别(两个等号与三个等号)

js中两个等号(==)和三个等号(===)的区别

在Excel中,如何在左侧的范围内找到相同的单词?

比较两个字典是不是相等