从一行中的两个列表创建字典
Create a dictionary from two lists in a one-liner
从这些列表中,
lst1 = ['a','b','c']
lst2 = [[1,2],[3,4,5],[6,7]]
我要创建:
{1: 'a', 3: 'b', 6: 'c', 2: 'a', 4: 'b', 7: 'c', 5: 'b'}
我可以使用 for 循环创建它:
d = {}
for l, nums in zip(lst1, lst2):
for num in nums:
d[num] = l
我想我需要使用 map 和 zip,所以我尝试了,
dict(map(lambda x: dict(x), zip(lst2,lst1)))
但这给出了
ValueError: dictionary update sequence element #1 has length 1; 2 is required.
我认为问题是 lst2
是一个列表列表,但我不知道如何继续。
使用理解:
d = {num:l for l, nums in zip(lst1, lst2) for num in nums}
因为有一个嵌套循环,你需要一些东西来扩展第二个列表,它可以用像 itertools.chain
这样的东西但是比仅仅写出循环要复杂得多,无论你是在理解或你现在正在做的。
编辑:
您的预期输出与循环产生的输出不同。如果您想在一个班轮中获得预期的输出(当然不包括进口),这是一种解决方案。
(i) 基于@mozway 使用 itertools.zip_longest
跨子列表创建元组 zip 对象的想法,使用 dict.fromkeys
方法从这些元组和 lst1
创建字典,
(ii) 使用 map
和字典理解,从 (i)
创建字典列表
(iii) 使用 functools.reduce
,将列表展平为单个字典
from itertools import zip_longest
from functools import reduce
out = reduce(lambda d, x: {**d, **x}, map(lambda kv: {k:v for k,v in zip(*kv) if k is not None}, dict.fromkeys(zip_longest(*lst2), lst1).items()))
另一个解决方案:
from itertools import zip_longest, starmap
from functools import reduce
out = dict(reduce(lambda tp, x: tp+tuple(filter(None, x)), zip_longest(*map(dict.items, starmap(dict.fromkeys, zip(lst2, lst1))))))
输出:
{1: 'a', 3: 'b', 6: 'c', 2: 'a', 4: 'b', 7: 'c', 5: 'b'}
旧解决方案(如果顺序无关紧要):
正如您所说,问题在于 lst1
是一个列表,而 lst2
是一个列表列表。我们可以使用另一个 zip 从 lst1
创建子列表。然后使用 functools.reduce
来展平字典列表:
from functools import reduce
out = reduce(lambda d, x: {**d, **x},
map(lambda x: dict(zip(x[0], [x[1]]*len(x[0]))), zip(lst2,lst1)))
或者使用@Tadhg 的优秀建议使用 itertools.starmap
和 dict.fromkeys
方法,你也可以这样做:
from itertools import starmap
out = reduce(lambda d,x: {**d, **x}, starmap(dict.fromkeys, zip(lst2,lst1)))
或使用collections.ChainMap
(更简单):
from collections import ChainMap
out = ChainMap(*starmap(dict.fromkeys, zip(lst2,lst1)))
输出:
{1: 'a', 2: 'a', 3: 'b', 4: 'b', 5: 'b', 6: 'c', 7: 'c'}
为了好玩,只是一个具有字典理解的变体(类似于@Tadhg 已经提出的),但保留子列表索引的顺序:
from itertools import zip_longest
out = {k: v for v, *keys in zip_longest(lst1, *lst2)
for k in keys if k is not None}
输出:
{1: 'a', 3: 'a', 6: 'a', 2: 'b', 4: 'b', 7: 'b', 5: 'c'}
从这些列表中,
lst1 = ['a','b','c']
lst2 = [[1,2],[3,4,5],[6,7]]
我要创建:
{1: 'a', 3: 'b', 6: 'c', 2: 'a', 4: 'b', 7: 'c', 5: 'b'}
我可以使用 for 循环创建它:
d = {}
for l, nums in zip(lst1, lst2):
for num in nums:
d[num] = l
我想我需要使用 map 和 zip,所以我尝试了,
dict(map(lambda x: dict(x), zip(lst2,lst1)))
但这给出了
ValueError: dictionary update sequence element #1 has length 1; 2 is required.
我认为问题是 lst2
是一个列表列表,但我不知道如何继续。
使用理解:
d = {num:l for l, nums in zip(lst1, lst2) for num in nums}
因为有一个嵌套循环,你需要一些东西来扩展第二个列表,它可以用像 itertools.chain
这样的东西但是比仅仅写出循环要复杂得多,无论你是在理解或你现在正在做的。
编辑:
您的预期输出与循环产生的输出不同。如果您想在一个班轮中获得预期的输出(当然不包括进口),这是一种解决方案。
(i) 基于@mozway 使用 itertools.zip_longest
跨子列表创建元组 zip 对象的想法,使用 dict.fromkeys
方法从这些元组和 lst1
创建字典,
(ii) 使用 map
和字典理解,从 (i)
(iii) 使用 functools.reduce
,将列表展平为单个字典
from itertools import zip_longest
from functools import reduce
out = reduce(lambda d, x: {**d, **x}, map(lambda kv: {k:v for k,v in zip(*kv) if k is not None}, dict.fromkeys(zip_longest(*lst2), lst1).items()))
另一个解决方案:
from itertools import zip_longest, starmap
from functools import reduce
out = dict(reduce(lambda tp, x: tp+tuple(filter(None, x)), zip_longest(*map(dict.items, starmap(dict.fromkeys, zip(lst2, lst1))))))
输出:
{1: 'a', 3: 'b', 6: 'c', 2: 'a', 4: 'b', 7: 'c', 5: 'b'}
旧解决方案(如果顺序无关紧要):
正如您所说,问题在于 lst1
是一个列表,而 lst2
是一个列表列表。我们可以使用另一个 zip 从 lst1
创建子列表。然后使用 functools.reduce
来展平字典列表:
from functools import reduce
out = reduce(lambda d, x: {**d, **x},
map(lambda x: dict(zip(x[0], [x[1]]*len(x[0]))), zip(lst2,lst1)))
或者使用@Tadhg 的优秀建议使用 itertools.starmap
和 dict.fromkeys
方法,你也可以这样做:
from itertools import starmap
out = reduce(lambda d,x: {**d, **x}, starmap(dict.fromkeys, zip(lst2,lst1)))
或使用collections.ChainMap
(更简单):
from collections import ChainMap
out = ChainMap(*starmap(dict.fromkeys, zip(lst2,lst1)))
输出:
{1: 'a', 2: 'a', 3: 'b', 4: 'b', 5: 'b', 6: 'c', 7: 'c'}
为了好玩,只是一个具有字典理解的变体(类似于@Tadhg 已经提出的),但保留子列表索引的顺序:
from itertools import zip_longest
out = {k: v for v, *keys in zip_longest(lst1, *lst2)
for k in keys if k is not None}
输出:
{1: 'a', 3: 'a', 6: 'a', 2: 'b', 4: 'b', 7: 'b', 5: 'c'}