1

Imagine we have a list of lists where the nested list contains two elements [A, B]. Now, you want to convert such list into a dictionary where the first element should be the key and the second element should be its value. For example:

[['A', 'B'], ['A', 'C'], ['A', 'D'], ['B', 'B1'], ['B', 'C1'], ['C', 'B2'], ['C', 'C2'], ['C', 'D2']] -->

{'A': ['B', 'C', 'D'], 'B': ['B1', 'C1'], 'C': ['B2', 'C2', 'D2']}

The order of the values should be kept.

I have a straightforward solution:

lst = [['A', 'B'], ['A', 'C'], ['A', 'D'], ['B', 'B1'], ['C', 'C1']]
dic = {}
for el in lst:
    if dic.get(el[0]) is None:
        dic[el[0]] = [el[1]]
        continue
    dic[el[0]].append(el[1])

And an ugly list comprehension solution which is significantly slower because it performs a loop over complete data for each single key.

dic = dict([(el[0], [e[1] for r in lst if e[0] == el[0]])
            for el in lst])

There should be a better and more elegant way to do this. Can you come up with something?

timgeb
  • 76,762
  • 20
  • 123
  • 145
minerals
  • 6,090
  • 17
  • 62
  • 107

3 Answers3

6

Use a defaultdict:

from collections import defaultdict
dic = defaultdict(list)
for k, v in lst:
    dic[k].append(v)
Daniel Roseman
  • 588,541
  • 66
  • 880
  • 895
2

An alternative to defaultdict is to use the .setdefault method of a plain dict.

lst = [
    ['A', 'B'], ['A', 'C'], ['A', 'D'], ['B', 'B1'],
    ['B', 'C1'], ['C', 'B2'], ['C', 'C2'], ['C', 'D2'],
]

dic = {}
for key, val in lst:
    dic.setdefault(key, []).append(val)
print(dic)

output

{'A': ['B', 'C', 'D'], 'B': ['B1', 'C1'], 'C': ['B2', 'C2', 'D2']}

This will preserve the order of the values in all versions of Python, and in Python 3.6+ it will also preserve the order of the keys.

PM 2Ring
  • 54,345
  • 6
  • 82
  • 182
  • 2
    yes, and it's a duplicate answered many times – Jean-François Fabre Apr 27 '18 at 12:06
  • @Jean-FrançoisFabre Fair enough. But couldn't you find a good dupe target that uses `.append` instead of `.extend`? Sure, they're similar, but the extend case used in your target is a little more complex than doing a simple append. – PM 2Ring Apr 27 '18 at 12:18
  • I've seen that a lot of times. It's just that I cannot find the dupes I've seen right now. – Jean-François Fabre Apr 27 '18 at 12:19
  • ah found a better one. That said, I like this particular question. It's clear, as opposed to the questions linked. I may not have found the perfect dupe, but there _is_ one :) – Jean-François Fabre Apr 27 '18 at 12:21
  • @Jean-FrançoisFabre It's tricky to find a good dupe for this. The questions tend to be fairly specific, and while an expert may be able to apply the code from a different question it might not be so easy for a newer coder, so it's easier to just write a fresh answer. – PM 2Ring Apr 27 '18 at 12:22
  • @Jean-FrançoisFabre If all else fails, this one can become the dupe target. :D – PM 2Ring Apr 27 '18 at 12:23
  • Yeah what the hell. Bookmarked it. – Jean-François Fabre Apr 27 '18 at 12:28
0

Another approach would be this:

l = [['A', 'B'], ['A', 'C'], ['A', 'D'], ['B', 'B1'], ['B', 'C1'], ['C', 'B2'], ['C', 'C2'], ['C', 'D2']]

d = dict((elem[0],[]) for elem in l)
for elem in l:
    d[elem[0]].append(elem[1])
print(d)

Output:

{'A': ['B', 'C', 'D'], 'B': ['B1', 'C1'], 'C': ['B2', 'C2', 'D2']}
Vasilis G.
  • 7,556
  • 4
  • 19
  • 29