Python: Way to build a dictionary with a variable key and append to a list as the value inside a loop

Question:

I have a list of dictionaries. I want to loop through this list of dictionary and for each specific name (an attribute inside each dictionary), I want to create a dictionary where the key is the name and the value of this key is a list which dynamically appends to the list in accordance with a specific condition.

For example, I have

d = [{'Name': 'John', 'id': 10},
     {'Name': 'Mark', 'id': 21},
     {'Name': 'Matthew', 'id': 30},
     {'Name': 'Luke', 'id': 11},
     {'Name': 'John', 'id': 20}]

I then built a list with only the names using names=[i[‘Name’] for i in dic1] so I have a list of names. Notice John will appear twice in this list (at the beginning and end). Then, I want to create a for-loop (for name in names), which creates a dictionary ‘ID’ that for its value is a list which appends this id field as it goes along.

So in the end I’m looking for this ID dictionary to have:

John: [10,20]
Mark: [21]
Matthew: [30]
Luke: [11]

Notice that John has a list length of two because his name appears twice in the list of dictionaries.

But I can’t figure out a way to dynamically append these values to a list inside the for-loop. I tried:

 ID={[]} #I also tried with just {}
 for name in names: 
            ID[names].append([i['id'] for i in dic1 if i['Name'] == name])

Please let me know how one can accomplish this. Thanks.

Asked By: Prospero

||

Answers:

You can use defaultdict for this to initiate a dictionary with a default value. In this case the default value will be empty list.

from collections import defaultdict
d=defaultdict(list)
for item in dic1:
    d[item['Name']].append(item['id'])

Output

{'John': [10, 20], 'Mark': [21], 'Matthew': [30], 'Luke': [11]} # by converting (not required) into pure dict dict(d)
Answered By: mad_

Don’t loop over the list of names and go searching for every one in the list; that’s very inefficient, since you’re scanning the whole list all over again for every name. Just loop over the original list once and update the ID dict as you go. Also, if you build the ID dict first, then you can get the list of names from it and avoid another list traversal:

names = ID.keys()

The easiest solution for ID itself is a dictionary with a default value of the empty list; that way ID[name].append will work for names that aren’t in the dict yet, instead of blowing up with a KeyError.

from collections import defaultdict
ID = defaultdict(list)
for item in d:
    ID[item['Name']].append(item['id'])

You can treat a defaultdict like a normal dict for almost every purpose, but if you need to, you can turn it into a plain dict by calling dict on it:

plain_id = dict(ID)

The Thonnu has a solution using get and list concatenation which works without defaultdict. Here’s another take on a no-import solution:

ID = {}
for item in d:
    name, number = item['Name'], item['id']
    if name in ID:
        ID[name].append(number)
    else:
        ID[name] = [ number ]
Answered By: Mark Reed
dic1 = [{'Name': 'John', 'id': 10}, {'Name': 'Mark', 'id': 21}, {'Name': 'Matthew', 'id': 30}, {'Name': 'Luke', 'id': 11}, {'Name': 'John', 'id': 20}]

id_dict = {}
for dic in dic1:
    key = dic['Name']
    if key in id_dict:
        id_dict[key].append(dic['id'])
    else:
        id_dict[key] = [dic['id']]

print(id_dict)   # {'John': [10, 20], 'Mark': [21], 'Matthew': [30], 'Luke': [11]}
Answered By: mauro

Using collections.defaultdict:

from collections import defaultdict
out = defaultdict(list)
for item in dic1:
    out[item['Name']].append(item['id'])
print(dict(out))

Or, without any imports:

out = {}
for item in dic1:
    out[item['Name']] = out.get(item['Name'], []) + [item['id']]
print(out)

Or, with a list comprehension:

out = {}
[out.update({item['Name']: out.get(item['Name'], []) + [item['id']]}) for item in dic1]
print(out)

Output:

{'John': [10, 20], 'Mark': [21], 'Matthew': [30], 'Luke': [11]}
Answered By: The Thonnu

You can do in a easy version

dic1=[{'Name': 'John', 'id':10}, {'Name': 'Mark', 'id':21},{'Name': 'Matthew', 'id':30}, {'Name': 'Luke', 'id':11}, {'Name': 'John', 'id':20}]
names=[i['Name'] for i in dic1]
ID = {}
for i, name in enumerate(names):
    if name in ID:
        ID[name].append(dic1[i]['id'])
    else:
        ID[name] = [dic1[i]['id']]
print(ID)
Answered By: Tanus Szabo