Create a dictionary from two lists in a one-liner

882 views Asked by At

From these lists,

lst1 = ['a','b','c']
lst2 = [[1,2],[3,4,5],[6,7]]

I want to create:

{1: 'a', 3: 'b', 6: 'c', 2: 'a', 4: 'b', 7: 'c', 5: 'b'}

I can create it using a for loop:

d = {}
for l, nums in zip(lst1, lst2):
    for num in nums:
        d[num] = l

I figured I need to use map and zip, so I tried,

dict(map(lambda x: dict(x), zip(lst2,lst1)))

but this gives

ValueError: dictionary update sequence element #1 has length 1; 2 is required.

I think the issue is the lst2 is a list of lists but I just don't know how to proceed.

3

There are 3 answers

4
AudioBubble On BEST ANSWER

EDIT:

Your expected output is different from the output produced by your loop. If you want to get the expected output in a one liner (excluding imports of course), here is one solution.

(i) Building on @mozway's idea of using itertools.zip_longest to create zip object of tuples across sublists, use dict.fromkeys method to create a dictionary from these tuples and lst1,

(ii) Using map and a dict comprehension, create a list of dictionaries from (i)

(iii) Using functools.reduce, flatten the list to a single dictionary

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()))

Another solution:

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))))))

Output:

{1: 'a', 3: 'b', 6: 'c', 2: 'a', 4: 'b', 7: 'c', 5: 'b'}

Old solution (if order doesn't matter):

As you say, the issue is that lst1 is a list and lst2 is a list of lists. We can use another zip to create sublists from lst1. Then use functools.reduce to flatten the list of dictionaries:

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)))

Or using @Tadhg's excellent suggestion to use itertools.starmap and dict.fromkeys methods, you can also do:

from itertools import starmap
out = reduce(lambda d,x: {**d, **x}, starmap(dict.fromkeys, zip(lst2,lst1)))

or using collections.ChainMap (even simpler):

from collections import ChainMap
out = ChainMap(*starmap(dict.fromkeys, zip(lst2,lst1)))

Output:

{1: 'a', 2: 'a', 3: 'b', 4: 'b', 5: 'b', 6: 'c', 7: 'c'}
0
Tadhg McDonald-Jensen On

use a comprehension:

d = {num:l for l, nums in zip(lst1, lst2) for num in nums}

since there is a nested loop you need something to expand the second list, it'd be possible with something like itertools.chain but much more complicated than just writing out the loops, whether you do it in a comprehension or as you are doing it now.

0
mozway On

For fun, just a variant with a dictionary comprehension (similar to what already proposed by @Tadhg), but preserving the order of the indices of the sublists:

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}

ouput:

{1: 'a', 3: 'a', 6: 'a', 2: 'b', 4: 'b', 7: 'b', 5: 'c'}