问题描述
# my Noah's Ark
myanimals = (('cat','dog'),('callitrix','platypus'),('anaconda','python'),('mouse','girafe'),...,('platypus','callitrix'))
因为我想要一个唯一的 2 元组动物列表,所以这对 ('platypus','callitrix') 被认为是 ('callitrix','platypus') 的副本。
如何优雅地从 myanimals 中(用最少的代码)删除 (a,b) 的所有类型的 (b,a) 重复项?
解决方法
我将分两部分回答:
- 严格来说并不是对您问题的回答,而是一个可以让您更轻松地解决问题的建议:如果您的代码允许使用
set
而不是tuple
,您可以使用关键字in
来检查您需要什么:
myanimals = ({'cat','dog'},{'callitrix','platypus'},{'anaconda','python'},{'mouse','girafe'},... {('platypus','callitrix')})
{'platypus','callitrix'} in myanimals # returns True,since {'a','b'}=={'b','a'}
因此,制作一组集合将自动删除重复项:
myanimals = {{'cat',...,{'platypus','callitrix'} }
将自动删除重复的 {'platypus','callitrix'}
。
然而,这样做意味着你不能让成对的动物是相同的两只动物,因为 {'a','a'}
只是 {'a'}
。
- 实际上使用元组有点麻烦。由于元组是不可变的,因此您需要从头开始创建一个新元组,并在此过程中过滤掉重复项:
myanimals = (('cat','dog'),('callitrix','platypus'),('anaconda','python'),('mouse','girafe'),('platypus','callitrix'))
myanimals_clean = []
for pair in myanimals:
if pair not in myanimals_clean and (pair[1],pair[0]) not in myanimal_clean:
myanimals_clean.append(pair)
您可以使用 itertools.permutations()
稍微清理一下,但我认为不值得额外导入的麻烦。
最后,您可以混合两种答案,将元组元组转换为集合元组以进行检查,然后再转换回元组:
myanimals = tuple( (set(pair) for pair in myanimals) )
myanimals = tuple( (tuple(pair) for pair in myanimals if pair not in myanimals) )
,
您可以在已排序的元组值上使用集合,或将列表转换为字典,其中键是按排序顺序的元组。这样每个组合只会留下一个值:
list({*map(tuple,map(sorted,myanimals))})
或
list(dict(zip(map(tuple,myanimals)),myanimals)).values())
分解
[*map(sorted,myanimals)] # sorted tuples
# [['cat','dog'],['callitrix','platypus'],['anaconda','python'],['girafe','mouse'],'platypus']]
# notice that both ('callitrix','platypus') and ('platypus','callitrix')
# are converted to ('callitrix','platypus')
因为这给出了一个列表列表并且字典键需要是可散列的,我们将项目转换为元组:
[*map(tuple,myanimals))]
# [('cat',('girafe','mouse'),'platypus')]
这些已经可以通过将其放入集合并将集合转换回列表来转换为唯一对的列表:
list({*map(tuple,myanimals))})
# [('girafe',('cat','dog')]
如果您不关心每个元组中值的原始顺序,您可以就此打住。但是,如果您需要 ('mouse','girafe') 保持该顺序,那么我们需要一个额外的步骤来将唯一性过滤与元组内容分开。这就是字典的用武之地。我们希望使用这些排序的元组作为键,但保留原始顺序作为值。 zip 函数通过将关键部分与原始元组组合来实现这一点:
[*zip(map(tuple,myanimals)]
# [(('cat','dog')),(('callitrix','platypus')),(('anaconda','python')),(('girafe','girafe')),'callitrix'))]
将其输入字典只会保留每个不同键的最后一个值,我们可以简单地提取这些值以形成元组结果列表:
list(dict(zip(map(tuple,myanimals)).values())
[('cat','callitrix'),'girafe')]
或者
请注意,上面选择的 ('platypus','callitrix') over ('platypus','callitrix') 因为它保留了最后一次出现的重复条目。
如果您需要保留第一次出现,您可以使用不同的方法,根据每个元组首次添加到集合中,逐步填充一组元组顺序和过滤器。
[t for s in [{myanimals}] for t in myanimals
if t not in s and not s.update((t,t[::-1]))]
# [('cat','girafe')]