Python:如果第一个元素与其他元素

时间:2018-02-04 13:21:40

标签: python tuples

如果之前有人问过,请道歉,但我无法找到它。如果我有类似的东西:

lst = [(('a', 'b'), 1, 2), (('a', 'b'), 3, 4), (('b', 'c'), 5, 6)]

我想获得一个更短的清单:

new = [(('a', 'b'), (1, 3), (2, 4)), (('b', 'c'), 5, 6)]

所以它通过第一个匹配元素将元组中的其他元素组合在一起,最快的方法是什么?

3 个答案:

答案 0 :(得分:4)

您基于密钥分组。如果输入组始终是连续的,则可以使用itertools.groupby(),否则使用字典对元素进行分组。如果订单很重要,请使用保留插入顺序的字典(> Python 3.6 dictcollections.OrderedDict)。

使用groupby()

from itertools import groupby
from operator import itemgetter

new = [(k, *zip(*(t[1:] for t in g))) for k, g in groupby(lst, key=itemgetter(0))]

以上使用Python 3语法从迭代(...,* iterable)中插入元组元素。

使用字典:

groups = {}
for key, *values in lst:
    groups.setdefault(key, []).append(values)
new = [(k, *zip(*v)) for k, v in groups.items()]

在Python 3.6或更新版本中,它将保留组的输入顺序。

演示:

>>> from itertools import groupby
>>> from operator import itemgetter
>>> lst = [(('a', 'b'), 1, 2), (('a', 'b'), 3, 4), (('b', 'c'), 5, 6)]
>>> [(k, *zip(*(t[1:] for t in g))) for k, g in groupby(lst, key=itemgetter(0))]
[(('a', 'b'), (1, 3), (2, 4)), (('b', 'c'), (5,), (6,))]
>>> groups = {}
>>> for key, *values in lst:
...     groups.setdefault(key, []).append(values)
...
>>> [(k, *zip(*v)) for k, v in groups.items()]
[(('a', 'b'), (1, 3), (2, 4)), (('b', 'c'), (5,), (6,))]

如果您使用的是Python 2,则必须使用:

new = [(k,) + tuple(zip(*(t[1:] for t in g))) for k, g in groupby(lst, key=itemgetter(0))]

from collections import OrderedDict
groups = OrderedDict()
for entry in lst:
    groups.setdefault(entry[0], []).append(entry[1:])
new = [(k,) + tuple(zip(*v)) for k, v in groups.items()]

答案 1 :(得分:1)

您还可以使用collections.defaultdict对元组键进行分组:

from collections import defaultdict

lst = [(('a', 'b'), 1, 2), (('a', 'b'), 3, 4), (('b', 'c'), 5, 6)]

d = defaultdict(tuple)
for tup, fst, snd in lst:
    d[tup] += fst, snd
# defaultdict(<class 'tuple'>, {('a', 'b'): (1, 2, 3, 4), ('b', 'c'): (5, 6)})

for key, value in d.items():
    d[key] = value[0::2], value[1::2]
# defaultdict(<class 'tuple'>, {('a', 'b'): ((1, 3), (2, 4)), ('b', 'c'): ((5,), (6,))})

result = [(k, v1, v2) for k, (v1, v2) in d.items()]

哪个输出:

[(('a', 'b'), (1, 3), (2, 4)), (('b', 'c'), (5,), (6,))]

上述代码的逻辑:

  • 将元组分组为defaultdict元组。
  • 通过切片[0::2][1::2]将值拆分为第一和第二。
  • 使用列表推导将此更新的字典包装到正确的元组结构中。

答案 2 :(得分:0)

根据您的使用情况,您可能会发现使用字典或defaultdict更有用。它也会更好地扩展。

from collections import defaultdict
listmaker = lambda: ([],[]) # makes a tuple of 2 lists for the values.
my_data = defaultdict(listmaker) 
for letter_tuple, v1, v2 in lst:
    my_data[letter_tuple][0].append(v1)
    my_data[letter_tuple][1].append(v2)

然后,您将获得每个唯一(x,y)键的新元组列表。 Python处理检查以查看密钥是否已经存在并且速度很快。如果你绝对需要它作为一个列表,你也可以随时转换它:

new = [(k, tuple(v1s), tuple(v2s)) for k, (v1s, v2s) in my_data.items()]

此列表理解有点不透明,但它会将您的字典解压缩为指定的格式[(('a', 'b'), (1,3), (2,4)), ... ]