识别列表列表中具有3个共同点的列表


问题内容

我有一个清单清单。如果存在具有相同前三个元素的子列表,请将它们合并为一个列表并添加所有第四个元素。

最好用代码和所需的输出来解释该问题。

a_list = [['apple', 50, 60, 7],
          ['orange', 70, 50, 8],
          ['apple', 50, 60, 12]]

# output:
# [['apple', 50, 60, 19], ['orange', 70, 50, 8]]

我已经有一个类似问题的代码(前一段时间由Stack
Overflow中的另一个用户提供给我),但是我不完全了解它,因此无法相应地对其进行修改。该代码的作用是检查第0个和第2个元素是否相同,如果相同,则合并子列表,并添加第1个和第3个元素:

import defaultdict
data = [['42x120x1800', 50, '50x90x800', 60],
        ['42x120x1800', 8, '50x90x800', 10],
        ['2x10x800', 5, '5x9x80', 6]]

d = defaultdict(lambda :[0, 0])
for sub_list in data:
    key = (sub_list[0], sub_list[2])
    d[key][0] += sub_list[1]
    d[key][1] += sub_list[3]

new_data = [[key[0], val[0], key[1], val[1]] for key, val in d.iteritems()]
# [['2x10x800', 5, '5x9x80', 6], ['42x120x1800', 58, '50x90x800', 70]]

应该如何修改代码以适合我的新问题?如果您也能抽出宝贵的时间对代码进行彻底的解释,我将不胜感激。


问题答案:

您可以使用相同的原理,方法是将前三个元素用作键,并将int用作默认值工厂defaultdict(这样您将获得0初始值):

from collections import defaultdict

a_list = [['apple', 50, 60, 7],
          ['orange', 70, 50, 8],
          ['apple', 50, 60, 12]]

d = defaultdict(int)
for sub_list in a_list:
    key = tuple(sub_list[:3])
    d[key] += sub_list[-1]

new_data = [list(k) + [v] for k, v in d.iteritems()]

如果您使用的是Python 3,则可以简化为:

d = defaultdict(int)
for *key, v in a_list:
    d[tuple(key)] += v

new_data = [list(k) + [v] for k, v in d.items()]

因为您可以使用加星标的目标从列表中获取所有“剩余”值,所以每个子列表主要分配给key,最后一个值分配给v,从而使循环变得更简单了(并且.iteritems()dict中没有方法Python
3,因为.items()已经是一个迭代器)。

所以,我们使用一个defaultdict使用0作为默认值,然后从第3个值产生的每个键(作为一个元组,所以你可以使用它作为一个字典的键)总结的最后一个值。

  • 因此,对于第一个项目,['apple', 50, 60, 7]我们创建一个key ('apple', 50, 60),在d其中查找键(该键不存在,但是defaultdictint()用于创建新值0),然后7从该第一项中添加。

  • ('orange', 70, 50)键和值执行相同的操作8

  • 对于第3个项目,我们('apple', 50, 60)再次获取密钥,并将其添加12到中已存在的密钥7d[('apple', 50, 60)]。共19个

然后,我们将(键,值)对重新放入列表中,您就完成了。结果是:

>>> new_data
[['apple', 50, 60, 19], ['orange', 70, 50, 8]]

需要对数据进行排序的另一种实现方式是itertools.groupby

from itertools import groupby
from operator import itemgetter

a_list = [['apple', 50, 60, 7],
          ['orange', 70, 50, 8],
          ['apple', 50, 60, 12]]

newlist = [list(key) + [sum(i[-1] for i in sublists)] 
    for key, sublists in groupby(sorted(a_list), key=itemgetter(0, 1, 2))]

对于相同的输出。如果您的数据未排序,这会变慢,但是很高兴知道不同的方法。