列表理解,检查项目是否唯一

时间:2015-05-19 17:07:15

标签: python list dictionary list-comprehension

我正在尝试编写一个列表理解语句,如果列表当前未包含该项,则该语句只会添加一个项目。有没有办法检查当前正在构建的列表中的当前项?这是一个简短的例子:

输入

{
    "Stefan" : ["running", "engineering", "dancing"],
    "Bob" : ["dancing", "art", "theatre"],
    "Julia" : ["running", "music", "art"]
}

输出

["running", "engineering", "dancing", "art", "theatre", "music"]

不使用列表推导的代码

output = []
for name, hobbies in input.items():
    for hobby in hobbies:
        if hobby not in output:
            output.append(hobby)

我的尝试

[hobby for name, hobbies in input.items() for hobby in hobbies if hobby not in ???]

8 个答案:

答案 0 :(得分:34)

您可以使用set并设置理解:

{hobby for name, hobbies in input.items() for hobby in hobbies}

作为m.wasowski mentioned,我们不在此使用name,因此我们可以使用item.values()代替:

{hobby for hobbies in input.values() for hobby in hobbies}

如果您确实需要一个列表作为结果,您可以这样做(但请注意,通常您可以使用集合而没有任何问题):

list({hobby for hobbies in input.values() for hobby in hobbies})

答案 1 :(得分:16)

正如this answer建议:您可以使用唯一性过滤器:

def f7(seq):
    seen = set()
    seen_add = seen.add
    return [x for x in seq if not (x in seen or seen_add(x))]

并致电:

>>> f7(hobby for name, hobbies in input.items() for hobby in hobbies)
['running', 'engineering', 'dancing', 'art', 'theatre', 'music']

我会分别实现唯一性过滤器,因为设计规则说“不同的东西应该由不同的类/方法/组件/无论什么”来处理。此外,如果需要,您可以简单地重复使用此方法。

另一个优点是 - 正如linked answer所写的那样 - 保留了项目的顺序。对于某些应用程序,这可能是必要的。

答案 2 :(得分:7)

集和词典是你的朋友:

from collections import OrderedDict
from itertools import chain # 'flattens' collection of iterables

data = {
    "Stefan" : ["running", "engineering", "dancing"],
    "Bob" : ["dancing", "art", "theatre"],
    "Julia" : ["running", "music", "art"]
}

# using set is the easiest way, but sets are unordered:
print {hobby for hobby in chain.from_iterable(data.values())}
# output:
# set(['art', 'theatre', 'dancing', 'engineering', 'running', 'music'])


# or use OrderedDict if you care about ordering:
print OrderedDict(
        (hobby, None) for hobby in chain.from_iterable(data.values())
    ).keys()
# output:
# ['dancing', 'art', 'theatre', 'running', 'engineering', 'music']

答案 3 :(得分:7)

如果你真的想要一个listcomp而且只需要一个list-comp,你可以做到

>>> s = []
>>> [s.append(j)  for i in d.values() for j in i if j not in s]
[None, None, None, None, None, None]
>>> s
['dancing', 'art', 'theatre', 'running', 'engineering', 'music']

此处,s是副作用的结果,d是您的原始字典。这里的独特优势在于您可以保留订单,而不像其他大多数答案

  

注意:这是一种糟糕的方式,因为它利用了list-comp,结果是副作用。不要将其作为一种练习,这个答案只是为了向您展示您可以单独使用列表补偿来实现它

答案 4 :(得分:6)

还有另一种写作方式,它更能描述你实际在做什么,并且不需要嵌套(双for)理解:

output = set.union(*[set(hobbies) for hobbies in input_.values()])

如果您表示输入更具概念性声音,即使用每个人的爱好设置(因为那里不应该重复),这就变得更好了:

input_ = {
    "Stefan" : {"running", "engineering", "dancing"},
    "Bob" : {"dancing", "art", "theatre"}, 
    "Julia" : {"running", "music", "art"}
}

output = set.union(*input_.values())

答案 5 :(得分:5)

列表理解不适合这个问题。我认为一套理解会更好,但由于这已经在另一个答案中显示,我将展示一种用紧凑的单行解决这个问题的方法:

list(set(sum(hobbies_dict.values(), [])))

另一个使用按位或运算符的有趣解决方案,它用作集合的联合运算符:

from operator import or_
from functools import reduce # Allowed, but unnecessary in Python 2.x
list(reduce(or_, map(set, hobbies_dict.values())))

或者(无意的双关语,我发誓),而不是使用按位或运算符,只需使用set.union并传递值的解压缩集映射。无需导入or_reduce!这个想法的灵感来自Thijs van Dien's answer

list(set.union(*map(set, hobbies_dict.values())))

答案 6 :(得分:4)

使用套装:

dict = {
    "Stefan" : ["running", "engineering", "dancing"],
    "Bob" : ["dancing", "art", "theatre"],
    "Julia" : ["running", "music", "art"]
}

myset = set()
for _, value in dict.items():
    for item in value:
        myset.add(item)

print(myset)

答案 7 :(得分:4)

这个怎么样:

set(dict['Bob']+dict['Stefan']+dict['Julia'])
>>> set(['art', 'theatre', 'dancing', 'engineering', 'running', 'music'])

或者更好:

dict = {
    "Stefan" : ["running", "engineering", "dancing"],
    "Bob" : ["dancing", "art", "theatre"],
    "Julia" : ["running", "music", "art"]
}

list_ = []
for y in dict.keys():
    list_ = list_ + dict[y]
list_ = set(list_)
>>> list_
set(['art', 'theatre', 'dancing', 'engineering', 'running', 'music'])

您可以将list函数应用于list_,如list(list_),以返回列表而非集合。