从元组字典中删除相似的元组

Remove similar tuple from dictionary of tuples

我有元组字典,如下所示: mydict = {0: (12, 89), 1: (23, 78), 2: (34, 67), 3: (45, 56), 4: (56, 45), 5: (67, 34), 6 : (78, 23), 7: (89, 12)}

这里,最后四个元素 (56, 45), (67, 34), (78, 23), (89, 12) 是前四个元素的副本,但排列顺序不同,并且 i想删除它。

我正在使用以下方法,但只有当元组相同时才会删除。例如:(12, 89) = (12, 89).

values = mydict.values()
for x, y in mydict.items():
   for i in values:
         if i not in mydict.items():
             print("Result", x, y)

获得预期结果的有效方法是什么?我想比较例如 (12, 89) 和 (89, 12),因为它有相同的元素,我想删除其中一个。

谁能帮我解决这个问题?

您的选择取决于您是否关心删除后元组的顺序以及您的输入字典是否有序 (python 3.6+)。

没有订购

解决方案 1 (Python 3.6+)

如果你不关心顺序并使用 python 3.6+,你可以使用以下技巧:

filtered = {tuple(sorted(val)): key for key, val in mydict.items().__reversed__()}
restored = {val: key for key, val in filtered.items()}
{0: (12, 89), 1: (23, 78), 2: (34, 67), 3: (45, 56)}

这个想法是建立在 python dict 在 3.6+ 上排序的概念之上的,这意味着第一个键将由于 __reversed__ 顺序而稍后插入到 dict 中。第一行颠倒了键和值,因此任何重复的值都将被覆盖(这就是为什么我们以相反的顺序进行操作,所以最后的元素被前面的元素覆盖)。第二行翻转键和值。

上述解决方案的重要说明是元组现在已排序。这意味着,如果你有 0: (89, 12),这将变成 0: (12, 89)

解决方案 2(任何版本)

第一个技巧实际上取决于可以通过用较低的键值替换较高的键值来消除它们的信息。为了确保该条件,我们可以通过根据排序值 (x[1]) 和它们的键 (x[0]) 对它们进行排序来生成有序结构。

ordered = sorted(mydict.items(), key=lambda x: (sorted(x[1]), x[0])).__reversed__()

这导致以下排序

[(4, (56, 45)), (3, (45, 56)), (5, (67, 34)), (2, (34, 67)), (6, (78, 23)), (1, (23, 78)), (7, (89, 12)), (0, (12, 89))]

然后重新应用解决方案1:

filtered = {tuple(sorted(val)): key for key, val in ordered}
restored = {val: key for key, val in filtered.items()}

我们完成了。

维持秩序

解决方案 1 (Python 3.6+)

filtered = {tuple(sorted(val)): key for key, val in mydict.items().__reversed__()}
restored = {val: mydict[val] for key, val in filtered.items()}

类似于第一个没有顺序的解决方案,但现在使用键从第一个字典中获取原始值。因此值将相同,换句话说 0: (89, 12),仍然是 0: (89, 12).

解决方案 2(任何版本)

与无序变体中的解决方案类似,我们重用原始字典来生成正确的索引。

ordered = sorted(mydict.items(), key=lambda x: (sorted(x[1]), x[0])).__reversed__()
filtered = {tuple(sorted(val)): key for key, val in ordered}
restored = {val: mydict[val] for key, val in filtered.items()}

备注

为了查看解决方案之间的差异,建议将 0: (12, 89) 的顺序交换为 0: (89, 12)

一切结合起来:

mydict = {0: (89, 12), 1: (23, 78), 2: (34, 67), 3: (45, 56), 4: (56, 45),
          5: (67, 34), 6: (78, 23), 7: (89, 12)}

if __name__ == '__main__':
    filtered = {tuple(sorted(val)): key for key, val in mydict.items().__reversed__()}
    restored = {val: key for key, val in filtered.items()}
    print(restored)

    restored = {val: mydict[val] for key, val in filtered.items()}
    print(restored)

    ordered = list(sorted(mydict.items(), key=lambda x: (sorted(x[1]), x[0])).__reversed__())
    filtered = {tuple(sorted(val)): key for key, val in ordered}
    restored = {val: key for key, val in filtered.items()}
    print(restored)

    ordered = sorted(mydict.items(), key=lambda x: (sorted(x[1]), x[0])).__reversed__()
    filtered = {tuple(sorted(val)): key for key, val in ordered}
    restored = {val: mydict[val] for key, val in filtered.items()}
    print(restored)

并且输出使用python 3.9:

{0: (12, 89), 1: (23, 78), 2: (34, 67), 3: (45, 56)}  # No order python 3.6+
{3: (45, 56), 2: (34, 67), 1: (23, 78), 0: (12, 89)}  # No order any version
{0: (89, 12), 1: (23, 78), 2: (34, 67), 3: (45, 56)}  # Order python 3.6+
{3: (45, 56), 2: (34, 67), 1: (23, 78), 0: (89, 12)}  # Order any version

编辑

Jasmijn 所示,当顺序无关紧要时,更合适的解决方案是将 tuple(sorted(val)) 替换为 frozenset(val)