37
[[1, '34', '44'], [1, '40', '30', '41'], [1, '41', '40', '42'], [1, '42', '41', '43'], [1, '43', '42', '44'], [1, '44', '34', '43']]

我有一个列表列表。我的目标是检查任何一个子列表是否与其他子列表有任何共同点(不包括要比较的第一个索引对象)。如果它有任何共同点,那么统一这些子列表。

例如,对于这个例子,我的最终答案应该是这样的:

[[1, '34, '44', '40' '30', '41', '42', '43']]

我可以理解我应该将子列表转换为集合,然后使用 union() 和 intersection() 操作。但我坚持的是如何比较每个集合/子列表。我无法在列表上运行循环并一一比较每个子列表,因为列表的内容将被修改,这会导致错误。

我想知道是否有任何有效的方法来比较所有子列表(转换为集合)并获得它们的联合?

4

7 回答 7

63

itertools模块可以解决这个问题:

>>> from itertools import chain
>>> list(set(chain.from_iterable(d)))
[1, '41', '42', '43', '40', '34', '30', '44']

另一种方法是将列表解压缩为union()的单独参数:

>>> list(set().union(*d))
[1, '41', '42', '43', '40', '34', '30', '44']

后一种方式消除了所有重复项,并且不需要首先将输入转换为集合。此外,它不需要导入。

于 2015-06-11T07:40:05.800 回答
39

使用解包操作符*

>> list(set().union(*a))
[1, '44', '30', '42', '43', '40', '41', '34']

(感谢 Raymond Hettinger 和 ShadowRanger 的评论!)

(注意

set.union(*tup)

将解压到

set.union(tup[0], tup[1], ... tup[n - 1])

)

于 2015-06-11T07:12:11.500 回答
1
In [20]: s
Out[20]: 
[[1, '34', '44'],
 [1, '40', '30', '41'],
 [1, '41', '40', '42'],
 [1, '42', '41', '43'],
 [1, '43', '42', '44'],
 [1, '44', '34', '43']]
In [31]: list({x for _list in s for x in _list})
Out[31]: [1, '44', '30', '42', '43', '40', '41', '34']

更新:

感谢您的评论

于 2015-06-11T07:13:01.643 回答
1

您可以使用 itertools 来执行此操作。让我们假设您的列表有一个变量名 A

import itertools

single_list_with_all_values = list(itertools.chain(*A))
single_list_with_all_values.sort()

print set(single_list_with_all_values)
于 2015-06-11T07:14:43.473 回答
1
>>> big = [[1, '34', '44'], [1, '40', '30', '41'], [1, '41', '40', '42'], [1, '42', '41', '43'], [1, '43', '42', '44'], [1, '44', '34', '43']]
>>> set(reduce ( lambda l,a : l + a, big))
set([1, '44', '30', '42', '43', '40', '41', '34'])

如果你真的想要一个列表作为最终结果

>>>>[list(set(reduce ( lambda l,a : l + a, big)))]
[[1, '44', '30', '42', '43', '40', '41', '34']]

如果您不喜欢为列表添加重新编码 lambda 函数:

>>>>[list(set(reduce ( list.__add__, big)))]
[[1, '44', '30', '42', '43', '40', '41', '34']]

编辑:在您建议使用 itertools.chain 而不是 list.__add__ 之后,我使用原始海报使用的原始变量为两者运行了 timeit。

似乎 timeit times list.__add__ 大约 2.8 秒和 itertools.chain 大约 3.5 秒。

我在这个页面上检查过,是的,你是对的,itertools.chain 包含一个 from_iterable 方法,它可以极大地提升性能。请参阅下面的 list.__add__、itertools.chain 和 itertools.chain.from_iterable。

>>> timeit.timeit("[list(set(reduce ( list.__add__, big)))]", setup="big = [ [10,20,30,40] for ele in range(10000)]", number=30)
16.051744650801993
>>> timeit.timeit("[list(set(reduce ( itertools.chain, big)))]", setup="big = [ [10,20,30,40] for ele in range(10000)]", number=30)
54.721315866467194
>>> timeit.timeit("list(set(itertools.chain.from_iterable(big)))", setup="big = [ [10,20,30,40] for ele in range(10000)]", number=30)
0.040056066849501804

非常感谢您的建议:)

于 2015-06-11T07:19:38.233 回答
0

仅用 python 2 测试:我个人喜欢 的可读性reduce,搭配一个简单的条件函数,比如

# PYTHON 2 ONLY!
somelists = [[1, '41', '40', '42'], [1, '42', '41', '43'], [1, '43', '42', '44'], [1, '44', '34', '43']] # your original lists
somesets = map(set,somelists) #your lists as sets

def condition(s1,s2): # condition to apply recursively to the sets
    if s1.intersection(s2):
        return s1.union(s2)
reduce( condition,somesets)
#{1, '30', '34', '40', '41', '42', '43', '44'}

当然,如果您愿意,您可以将此结果转换为 2d 列表list([reduce(...

我会注意到这比chain.fromiterable答案慢了 3 倍。

于 2015-07-30T22:33:40.193 回答
0
from functools import reduce

out = list(reduce(set.union, iterable))

只要至少第一个元素iterable是一个集合。否则,

out = list(reduce(set.union, iterable[1:], set(iterable[0])))
于 2020-04-28T23:28:16.070 回答