Python:从非唯一键和值列表创建列表字典的一行代码
Python: One-Liner to create a dictionary of lists from a list of non-unique keys and values
在我的编程经验中,我经常想从一个列表创建一个列表字典,就像这样:
输入:键值对列表,具有非唯一键,例如[("a", 1), ("a", 2), ("b", 3)]
输出:一个字典,其中每个非唯一键都有一个值列表,如 {"a": [1,2], "b": [3]}
.
现在我知道我可以像这样达到我想要的结果:
list_of_elems = [("a", 1), ("a", 2), ("b", 3)]
dict_of_elems = {}
for key, val in list_of_elems:
if key in dict_of_elems:
dict_of_elems[key].append(val)
else:
dict_of_elems[key] = [val]
(是的,我跳到这里之前看了看,但 EAFP 看起来基本一样)。
这很好用,但它是 6 行!我确信 python 中一定有一种方法可以使智能字典理解成为单行代码,但我想不出一个!有人有好主意吗?
实现这一目标的任何一行都将难以阅读并且可能很慢 (O(n^2)
)。我建议只使用您拥有的逻辑编写和重用函数。
我没有单行本,但这里有一个更简洁的版本:
list_of_elems = [("a", 1), ("a", 2), ("b", 3)]
dict_of_elems = {}
for key, val in list_of_elems:
dict_of_elems.setdefault(key, []).append(val)
一个 2 衬里,但缺点是 map
会产生一个与输入大小相等的无关列表。
dict_of_elms = {} # output dict
list( # force generator to be run
map( # apply lambda to every element
lambda tupElm : d[tupElm[0]].append(tupElm[1]) # append to corresponding dict key or create new list
if dict_of_elms.get(tupElm[0], None)
else dict_of_elms.setdefault(tupElm[0], [tupElm[1]]),
list_of_items # list of size-two tuples to iterate on
)
)
还值得注意的是,尽管可以在两行内压缩,但无论如何 space 都更容易...
一个班轮可以是:dict((k, [v for (kk, v) in list_of_elems if kk==k]) for (k,_ ) in list_of_elems)
然而正如 robinsax 所说,复杂性是最差的,Secret Agent 解决方案更可取。
这个 2-liner 使用 2 passes:
in_d = [("a", 1), ("a", 2), ("b", 3)]
output_dict = dict((e[0], []) for e in in_d)
_ = [output_dict[k].append(v) for (k, v) in in_d]
output_dict
# {'a': [1, 2], 'b': [3]}
Edit 正如正确指出的那样,上面修改字典作为副作用,更好的形式是实现显式循环:
in_d = [("a", 1), ("a", 2), ("b", 3)]
output_dict = dict((e[0], []) for e in in_d)
for (k, v) in in_d:
output_dict[k].append(v)
output_dict
# {'a': [1, 2], 'b': [3]}
使用 defaultdict 可以一次通过:
from collections import defaultdict
ddict = defaultdict(list)
for (k, v) in in_d:
ddict[k].append(v)
ddict
# {'a': [1, 2], 'b': [3]}
我唯一能想到的 one liner(使用普通工具而不使用副作用理解)是使用 groupby:
list_of_elems = [("a", 1), ("a", 2), ("b", 3)]
di={k:[t[1] for t in v] for k,v in groupby(sorted(list_of_elems),key=lambda t:t[0])}
>>> di
{'a': [1, 2], 'b': [3]}
算上就是两行from itertools import groupby
。由于 sorted
它比惯用语慢:
di={}
for k,v in list_of_elems:
di.setdefault(k, []).append(v)
或 defaultdict:
from collections import defaultdict
di=defaultdict(list)
for k,v in list_of_elems:
di[k].append(v)
>>> di
defaultdict(<class 'list'>, {'a': [1, 2], 'b': [3]})
或子类 dict
以便默认行为是 return 缺少键的列表(这实际上是 .setdefault
和 defaultdict
方法正在做的):
class Mydict(dict):
def __missing__(self, key):
self[key]=[]
return self[key]
di=Mydict()
for k,v in list_of_elems:
di[k].append(v)
在我的编程经验中,我经常想从一个列表创建一个列表字典,就像这样:
输入:键值对列表,具有非唯一键,例如[("a", 1), ("a", 2), ("b", 3)]
输出:一个字典,其中每个非唯一键都有一个值列表,如 {"a": [1,2], "b": [3]}
.
现在我知道我可以像这样达到我想要的结果:
list_of_elems = [("a", 1), ("a", 2), ("b", 3)]
dict_of_elems = {}
for key, val in list_of_elems:
if key in dict_of_elems:
dict_of_elems[key].append(val)
else:
dict_of_elems[key] = [val]
(是的,我跳到这里之前看了看,但 EAFP 看起来基本一样)。
这很好用,但它是 6 行!我确信 python 中一定有一种方法可以使智能字典理解成为单行代码,但我想不出一个!有人有好主意吗?
实现这一目标的任何一行都将难以阅读并且可能很慢 (O(n^2)
)。我建议只使用您拥有的逻辑编写和重用函数。
我没有单行本,但这里有一个更简洁的版本:
list_of_elems = [("a", 1), ("a", 2), ("b", 3)]
dict_of_elems = {}
for key, val in list_of_elems:
dict_of_elems.setdefault(key, []).append(val)
一个 2 衬里,但缺点是 map
会产生一个与输入大小相等的无关列表。
dict_of_elms = {} # output dict
list( # force generator to be run
map( # apply lambda to every element
lambda tupElm : d[tupElm[0]].append(tupElm[1]) # append to corresponding dict key or create new list
if dict_of_elms.get(tupElm[0], None)
else dict_of_elms.setdefault(tupElm[0], [tupElm[1]]),
list_of_items # list of size-two tuples to iterate on
)
)
还值得注意的是,尽管可以在两行内压缩,但无论如何 space 都更容易...
一个班轮可以是:dict((k, [v for (kk, v) in list_of_elems if kk==k]) for (k,_ ) in list_of_elems)
然而正如 robinsax 所说,复杂性是最差的,Secret Agent 解决方案更可取。
这个 2-liner 使用 2 passes:
in_d = [("a", 1), ("a", 2), ("b", 3)]
output_dict = dict((e[0], []) for e in in_d)
_ = [output_dict[k].append(v) for (k, v) in in_d]
output_dict
# {'a': [1, 2], 'b': [3]}
Edit 正如正确指出的那样,上面修改字典作为副作用,更好的形式是实现显式循环:
in_d = [("a", 1), ("a", 2), ("b", 3)]
output_dict = dict((e[0], []) for e in in_d)
for (k, v) in in_d:
output_dict[k].append(v)
output_dict
# {'a': [1, 2], 'b': [3]}
使用 defaultdict 可以一次通过:
from collections import defaultdict
ddict = defaultdict(list)
for (k, v) in in_d:
ddict[k].append(v)
ddict
# {'a': [1, 2], 'b': [3]}
我唯一能想到的 one liner(使用普通工具而不使用副作用理解)是使用 groupby:
list_of_elems = [("a", 1), ("a", 2), ("b", 3)]
di={k:[t[1] for t in v] for k,v in groupby(sorted(list_of_elems),key=lambda t:t[0])}
>>> di
{'a': [1, 2], 'b': [3]}
算上就是两行from itertools import groupby
。由于 sorted
它比惯用语慢:
di={}
for k,v in list_of_elems:
di.setdefault(k, []).append(v)
或 defaultdict:
from collections import defaultdict
di=defaultdict(list)
for k,v in list_of_elems:
di[k].append(v)
>>> di
defaultdict(<class 'list'>, {'a': [1, 2], 'b': [3]})
或子类 dict
以便默认行为是 return 缺少键的列表(这实际上是 .setdefault
和 defaultdict
方法正在做的):
class Mydict(dict):
def __missing__(self, key):
self[key]=[]
return self[key]
di=Mydict()
for k,v in list_of_elems:
di[k].append(v)