246

I want to create a dictionary whose values are lists. For example:

{
  1: ['1'],
  2: ['1','2'],
  3: ['2']
}

If I do:

d = dict()
a = ['1', '2']
for i in a:
    for j in range(int(i), int(i) + 2): 
        d[j].append(i)

I get a KeyError, because d[...] isn't a list. In this case, I can add the following code after the assignment of a to initialize the dictionary.

for x in range(1, 4):
    d[x] = list()

Is there a better way to do this? Lets say I don't know the keys I am going to need until I am in the second for loop. For example:

class relation:
    scope_list = list()
...
d = dict()
for relation in relation_list:
    for scope_item in relation.scope_list:
        d[scope_item].append(relation)

An alternative would then be replacing

d[scope_item].append(relation)

with

if d.has_key(scope_item):
    d[scope_item].append(relation)
else:
    d[scope_item] = [relation,]

What is the best way to handle this? Ideally, appending would "just work". Is there some way to express that I want a dictionary of empty lists, even if I don't know every key when I first create the list?

Johnathan Au
  • 5,244
  • 18
  • 70
  • 128
user118662
  • 2,713
  • 2
  • 17
  • 8

7 Answers7

314

You can use defaultdict:

>>> from collections import defaultdict
>>> d = defaultdict(list)
>>> a = ['1', '2']
>>> for i in a:
...   for j in range(int(i), int(i) + 2):
...     d[j].append(i)
...
>>> d
defaultdict(<type 'list'>, {1: ['1'], 2: ['1', '2'], 3: ['2']})
>>> d.items()
[(1, ['1']), (2, ['1', '2']), (3, ['2'])]
benklaasen
  • 53
  • 1
  • 6
mechanical_meat
  • 163,903
  • 24
  • 228
  • 223
60

You can build it with list comprehension like this:

>>> dict((i, range(int(i), int(i) + 2)) for i in ['1', '2'])
{'1': [1, 2], '2': [2, 3]}

And for the second part of your question use defaultdict

>>> from collections import defaultdict
>>> s = [('yellow', 1), ('blue', 2), ('yellow', 3), ('blue', 4), ('red', 1)]
>>> d = defaultdict(list)
>>> for k, v in s:
        d[k].append(v)

>>> d.items()
[('blue', [2, 4]), ('red', [1]), ('yellow', [1, 3])]
Nadia Alramli
  • 111,714
  • 37
  • 173
  • 152
41

You can use setdefault:

d = dict()
a = ['1', '2']
for i in a:
    for j in range(int(i), int(i) + 2): 
        d.setdefault(j, []).append(i)

print d  # prints {1: ['1'], 2: ['1', '2'], 3: ['2']}

The rather oddly-named setdefault function says "Get the value with this key, or if that key isn't there, add this value and then return it."

As others have rightly pointed out, defaultdict is a better and more modern choice. setdefault is still useful in older versions of Python (prior to 2.5).

Cimbali
  • 11,012
  • 1
  • 39
  • 68
RichieHindle
  • 272,464
  • 47
  • 358
  • 399
  • 2
    This works, but it's usually preferred to use defaultdict when it's available. – David Z Jun 06 '09 at 23:25
  • @David, yeah, setdefault wasn't the most brilliant bit of design, sorry -- it's hardly ever the best choice. I do think we (the Python committers) redeemed our collective reputation with collections.defaultdict, though;-). – Alex Martelli Jun 06 '09 at 23:46
  • @DavidZ, setdefault is different from defaultdict, as it's more flexible: otherwhise, how do you specify different default values for different dictionary keys? – Alex Gidan Jan 14 '16 at 13:37
  • @AlexGidan That's true, but not particularly relevant to this question. – David Z Jan 14 '16 at 13:47
  • This answer is also useful when you need an OrderedDict and a default value. – nimcap Apr 01 '19 at 12:10
3

Personally, I just use JSON to convert things to strings and back. Strings I understand.

import json
s = [('yellow', 1), ('blue', 2), ('yellow', 3), ('blue', 4), ('red', 1)]
mydict = {}
hash = json.dumps(s)
mydict[hash] = "whatever"
print mydict
#{'[["yellow", 1], ["blue", 2], ["yellow", 3], ["blue", 4], ["red", 1]]': 'whatever'}
john k
  • 6,268
  • 4
  • 55
  • 59
2

easy way is:

a = [1,2]
d = {}
for i in a:
  d[i]=[i, ]

print(d)
{'1': [1, ], '2':[2, ]}
Henning Lee
  • 544
  • 4
  • 13
2

Your question has already been answered, but IIRC you can replace lines like:

if d.has_key(scope_item):

with:

if scope_item in d:

That is, d references d.keys() in that construction. Sometimes defaultdict isn't the best option (for example, if you want to execute multiple lines of code after the else associated with the above if), and I find the in syntax easier to read.

spiffyman
  • 594
  • 4
  • 9
0

If you create it from a list, you can also use dict comprehension with a list comprehension within it:

dict_of_list_values = {len(k): [name for name in names if len(name) == len(k)] for k in names}

(I used a list of names for this example)