在忽略某些键的同时在列表中查找重复映射的 Pythonic 方法,并将重复映射组合成一个新列表
Pythonic way of finding duplicate maps in a list while ignoring certain keys, and combining the duplicate maps to make a new list
我想编写一个接受以下输入的代码:
list (list of maps)
request_keys (list of strings)
operation (add,substract,multiply,concat)
该代码将查看地图列表,以查找除 request_keys 中给出的键之外的所有键都具有相同值的映射。在找到两个匹配搜索键值的映射后,代码将对两个映射执行操作(添加、乘法、减法、连接)并将它们组合成一个映射。这张组合图基本可以替代另外两张图
我已经编写了以下代码来执行此操作。该代码仅执行添加操作。可以扩展它以进行其他操作
In [83]: list
Out[83]:
[{'a': 2, 'b': 3, 'c': 10},
{'a': 2, 'b': 3, 'c': 3},
{'a': 2, 'b': 4, 'c': 4},
{'a': 2, 'b': 3, 'c': 2},
{'a': 2, 'b': 3, 'c': 3}]
In [84]: %cpaste
Pasting code; enter '--' alone on the line to stop or use Ctrl-D.
:def func(list,request_keys):
: new_list = []
: found_indexes = []
: for i in range(0,len(list)):
: new_item = list[i]
: if i in found_indexes:
: continue
: for j in range(0,len(list)):
: if i != j and {k: v for k,v in list[i].iteritems() if k not in request_keys} == {k: v for k,v in list[j].iteritems() if k not in request_keys}:
: found_indexes.append(j)
: for request_key in request_keys:
: new_item[request_key] += list[j][request_key]
: new_list.append(new_item)
: return new_list
:--
In [85]: func(list,['c'])
Out[85]: [{'a': 2, 'b': 3, 'c': 18}, {'a': 2, 'b': 4, 'c': 4}]
In [86]:
我想知道的是,是否有更快、内存效率更高、更干净且更 pythonic 的方式来做同样的事情?
谢谢
您手动生成所有组合,然后比较每个组合。这很浪费。相反,我建议通过匹配键将字典分组到另一个字典中,然后添加 "same" 字典。另外,您忘记了 operator
参数。
import collections, operator, functools
def func(lst, request_keys, op=operator.add):
matching_dicts = collections.defaultdict(list)
for d in lst:
key = tuple(sorted(((k, d[k]) for k in d if k not in request_keys)))
matching_dicts[key].append(d)
for group in matching_dicts.values():
merged = dict(group[0])
merged.update({key: functools.reduce(op, (g[key] for g in group))
for key in request_keys})
yield merged
这是做什么的:首先,它创建一个字典,将两个字典必须相等的键值对映射到所有具有这些键值对的字典。然后它迭代这些组中的字典,使用该组中的一个作为原型并使用该组中所有字典的总和(或乘积,或其他,取决于运算符)更新它 required_keys
.
请注意,这 return 是一个生成器。如果您想要一个列表,只需像 list(func(...))
那样称呼它,或者在列表中累积 merged
个字典,然后 return 该列表。
from itertools import groupby
from operator import itemgetter
def mergeDic(inputData, request_keys):
keys = inputData[0].keys()
comparedKeys = [item for item in keys if item not in request_keys]
grouper = itemgetter(*comparedKeys)
result = []
for key, grp in groupby(sorted(inputData, key = grouper), grouper):
temp_dict = dict(zip(comparedKeys, key))
for request_key in request_keys:
temp_dict[request_key] = sum(item[request_key] for item in grp)
result.append(temp_dict)
return result
inputData = [{'a': 2, 'b': 3, 'c': 10},
{'a': 2, 'b': 3, 'c': 3},
{'a': 2, 'b': 4, 'c': 4},
{'a': 2, 'b': 3, 'c': 2},
{'a': 2, 'b': 3, 'c': 3}]
from pprint import pprint
pprint(mergeDic(inputData,['c']))
我想编写一个接受以下输入的代码:
list (list of maps)
request_keys (list of strings)
operation (add,substract,multiply,concat)
该代码将查看地图列表,以查找除 request_keys 中给出的键之外的所有键都具有相同值的映射。在找到两个匹配搜索键值的映射后,代码将对两个映射执行操作(添加、乘法、减法、连接)并将它们组合成一个映射。这张组合图基本可以替代另外两张图
我已经编写了以下代码来执行此操作。该代码仅执行添加操作。可以扩展它以进行其他操作
In [83]: list
Out[83]:
[{'a': 2, 'b': 3, 'c': 10},
{'a': 2, 'b': 3, 'c': 3},
{'a': 2, 'b': 4, 'c': 4},
{'a': 2, 'b': 3, 'c': 2},
{'a': 2, 'b': 3, 'c': 3}]
In [84]: %cpaste
Pasting code; enter '--' alone on the line to stop or use Ctrl-D.
:def func(list,request_keys):
: new_list = []
: found_indexes = []
: for i in range(0,len(list)):
: new_item = list[i]
: if i in found_indexes:
: continue
: for j in range(0,len(list)):
: if i != j and {k: v for k,v in list[i].iteritems() if k not in request_keys} == {k: v for k,v in list[j].iteritems() if k not in request_keys}:
: found_indexes.append(j)
: for request_key in request_keys:
: new_item[request_key] += list[j][request_key]
: new_list.append(new_item)
: return new_list
:--
In [85]: func(list,['c'])
Out[85]: [{'a': 2, 'b': 3, 'c': 18}, {'a': 2, 'b': 4, 'c': 4}]
In [86]:
我想知道的是,是否有更快、内存效率更高、更干净且更 pythonic 的方式来做同样的事情?
谢谢
您手动生成所有组合,然后比较每个组合。这很浪费。相反,我建议通过匹配键将字典分组到另一个字典中,然后添加 "same" 字典。另外,您忘记了 operator
参数。
import collections, operator, functools
def func(lst, request_keys, op=operator.add):
matching_dicts = collections.defaultdict(list)
for d in lst:
key = tuple(sorted(((k, d[k]) for k in d if k not in request_keys)))
matching_dicts[key].append(d)
for group in matching_dicts.values():
merged = dict(group[0])
merged.update({key: functools.reduce(op, (g[key] for g in group))
for key in request_keys})
yield merged
这是做什么的:首先,它创建一个字典,将两个字典必须相等的键值对映射到所有具有这些键值对的字典。然后它迭代这些组中的字典,使用该组中的一个作为原型并使用该组中所有字典的总和(或乘积,或其他,取决于运算符)更新它 required_keys
.
请注意,这 return 是一个生成器。如果您想要一个列表,只需像 list(func(...))
那样称呼它,或者在列表中累积 merged
个字典,然后 return 该列表。
from itertools import groupby
from operator import itemgetter
def mergeDic(inputData, request_keys):
keys = inputData[0].keys()
comparedKeys = [item for item in keys if item not in request_keys]
grouper = itemgetter(*comparedKeys)
result = []
for key, grp in groupby(sorted(inputData, key = grouper), grouper):
temp_dict = dict(zip(comparedKeys, key))
for request_key in request_keys:
temp_dict[request_key] = sum(item[request_key] for item in grp)
result.append(temp_dict)
return result
inputData = [{'a': 2, 'b': 3, 'c': 10},
{'a': 2, 'b': 3, 'c': 3},
{'a': 2, 'b': 4, 'c': 4},
{'a': 2, 'b': 3, 'c': 2},
{'a': 2, 'b': 3, 'c': 3}]
from pprint import pprint
pprint(mergeDic(inputData,['c']))