Adding key value pair to a dictionary list - python

I'm trying to add a third child to the below dictionary:
person = {'Name':'Jane', 'Age':32, 'Allergies':
[{'Allergen':'Dust'},
{'Allergen':'Feathers'},
{'Allergen':'Strawberries'}],
'Children':
[{'Name':'Ben', 'Age': 6}, {'Name':'Elly', 'Age': 8}]}
print(person)
{'Name': 'Jane', 'Age': 32, 'Allergies': [{'Allergen': 'Dust'}, {'Allergen': 'Feathers'}, {'Allergen': 'Strawberries'}], 'Children': [{'Name': 'Ben', 'Age': 6}, {'Name': 'Elly', 'Age': 8}]}
When I try update person.update('Children': [{'Name':'Hanna', 'Age':0}])
it replaces all children with just that one? Nothing else works either... Any suggestions?

The person dictionary does not know that the Allergies and Children are lists, so you need to use the lists' methods to append things to that specific list.
person["Allergies"].append({"Allergen": "gluten"})
# or
person["Children"].append({"name":"Hannah", "age": 0})

Related

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

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.
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 ]
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]}
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]}
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)
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)

How to ignore a single/multiple keys of all the dictionaries while looping over a list of dictionaries?

I am looping over a list of dictionaries and I have to drop/ignore either one or more keys of the each dictionary in the list and write it to a MongoDB. What is the efficient pythonic way of doing this ?
Example:
employees = [
{'name': "Tom", 'age': 10, 'salary': 10000, 'floor': 10},
{'name': "Mark", 'age': 5, 'salary': 12000, 'floor': 11},
{'name': "Pam", 'age': 7, 'salary': 9500, 'floor': 9}
]
Let's say I want to drop key = 'floor' or keys = ['floor', 'salary'].
Currently I am using del employees['floor'] inside the loop to delete the key and my_collection.insert_one() to simply write the dictionary into my MongoDB.
My code:
for d in employees:
del d['floor']
my_collection.insert_one(d)
The solution you proposed is the most efficient to use since you have no control on what happens inside the method insert_one.
If you have more keys, just loop over them:
ignored_keys = ['floor', 'salary']
for d in employees:
for k in ignored_keys:
del d[k]
my_collection.insert_one(d)
Let's say you want to drop keys = ['floor', 'salary']. You can try:
exclude_keys = ['salary', 'floor']
for d in employees:
my_collection.insert_one({k: d[k] for k in set(list(d.keys())) - set(exclude_keys)})

Create a List of Dictionaries using Comprehension

I currently have a list of strings that I am trying to create each string item into a dictionary object and store it within a list.
In attempting to create this list of dictionaries, I repeatedly create one big dictionary instead of iterating through item by item.
My code:
clothes_dict = [{clothes_list[i]: clothes_list[i + 1] for i in range(0, len(clothes_list), 2)}]
The error (All items being merged into one dictionary):
clothes_dict = {list: 1} [{'name': 'Tom', 'age': 10}, {'name': 'Mark', 'age': 5}, {'name': 'Pam', 'age': 7}]
0 = {dict: 2} {'name': 'Tom', 'age': 10}, {dict: 2} {'name': 'Mark', 'age': 5}, {'name': 'Pam', 'age': 7}```
Target Output (All Items being created into separate dictionaries within the single list):
clothes_dict = {list: 3} [{'name': 'Tom', 'age': 10}, {'name': 'Mark', 'age': 5}, {'name': 'Pam', 'age': 7}]
0 = {dict: 2} {'name': 'Tom', 'age': 10}
1 = {dict: 2} {'name': 'Mark', 'age': 5}
2 = {dict: 2} {'name': 'Pam', 'age': 7}```
I am attempting to make each entry within the list a new dictionary in the same form as the target output image.
clothes_dict = [{clothes_list[i]: clothes_list[i + 1]} for i in range(0, len(clothes_list), 2)]
You misplaced your closing right curly brace '}' in your list comprehension and placed it at the end which meant you was performing a dictionary comprehension as opposed to a list comprehension with each item being a dictionary.
Your code creates a list with a single dictionary:
clothes_dict = [{clothes_list[i]: clothes_list[i + 1] for i in range(0,l en(clothes_list), 2)}]
If you (for some reason) want a list of dictionaries with single entries:
clothes_dict = [{clothes_list[i]: clothes_list[i + 1]} for i in range(0,l en(clothes_list), 2)]
However, it seems to me that this may be a bit of an XY problem - in what case is a list of single-entry dictionaries the required format? Why not use a list of tuples for example?

How to inspect what is being passed to the filter() function

How can I verify the 'context' of a filter statement (normally I would just use a print in a function)? For example:
data=[{'Name':'Greg', 'Age': 10}, {'Name': 'Sarah', 'Age': 20}]
filter(lambda item: item['Name'] == 'Greg', data)
# [{'Age': 10, 'Name': 'Greg'}]
Instead of passing it a lambda, define a function that has the print statement and filter using that.
data=[{'Name':'Greg', 'Age': 10}, {'Name': 'Sarah', 'Age': 20}]
def my_filter(item):
print(f'from inside filter: {item}')
return item['Name'] == 'Greg'
print(list(filter(my_filter, data)))
I'm not going to claim that this is good practice, but for very quick debugging, you can use a tuple inside the lambda to shimmy a call to print in:
data=[{'Name':'Greg', 'Age': 10}, {'Name': 'Sarah', 'Age': 20}]
print(*filter(lambda item: (print(item), item['Name'] == 'Greg')[1], data))
{'Name': 'Greg', 'Age': 10}
{'Name': 'Sarah', 'Age': 20}
{'Name': 'Greg', 'Age': 10}
lambdas expects a single expression that will eventually be returned. If you want to add in "sequencing" of operations, you need to get a bit creative (although I don't recommend it for real, lasting code).
#juanpa.arrivillaga's idea is cleaner than mine:
lambda item: print(item) or item["Name"] == "Greg"
But it's the same idea. You need to put the print inside in such a way that the internal expression will evaluate in the end to item["Name"] == "Greg". I used the evaluation/indexing of a sequence to do that, while they used the behavior of or.

Obtain a set of unique values from a dictionary of lists

I have a list of dictionaries in which the dictionaries also contain a list.
I want to generate a set of the values of the respective nested lists so that I end up with a set of all of the unique items (in this case, hobbies).
I feel a set is perfect for this since it will automatically drop any duplicates, leaving me with a set of all of the unique hobbies.
people = [{'name': 'John', 'age': 47, 'hobbies': ['Python', 'cooking', 'reading']},
{'name': 'Mary', 'age': 16, 'hobbies': ['horses', 'cooking', 'art']},
{'name': 'Bob', 'age': 14, 'hobbies': ['Python', 'piano', 'cooking']},
{'name': 'Sally', 'age': 11, 'hobbies': ['biking', 'cooking']},
{'name': 'Mark', 'age': 54, 'hobbies': ['hiking', 'camping', 'Python', 'chess']},
{'name': 'Alisa', 'age': 52, 'hobbies': ['camping', 'reading']},
{'name': 'Megan', 'age': 21, 'hobbies': ['lizards', 'reading']},
{'name': 'Amanda', 'age': 19, 'hobbies': ['turtles']},
]
unique_hobbies = (item for item in people['hobbies'] for hobby in people['hobbies'].items())
print(unique_hobbies)
This generates an error:
TypeError: list indices must be integers or slices, not str
My comprehension is wrong, but I am not sure where. I want to iterate through each dictionary, then iterate through each nested list and update the items into the set, which will drop all duplicates, leaving me with a set of all of the unique hobbies.
You could also use a set-comprehension:
>>> unique_hobbies = {hobby for persondct in people for hobby in persondct['hobbies']}
>>> unique_hobbies
{'horses', 'lizards', 'cooking', 'art', 'biking', 'camping', 'reading', 'piano', 'hiking', 'turtles', 'Python', 'chess'}
The problem with your comprehension is that you want to access people['hobbies'] but people is a list and can only index lists with integers or slices. To make it work you need to iterate over you list and then access the 'hobbies' of each of the subdicts (like I did inside the set-comprehension above).
I got it:
unique_hobbies = set()
for d in people:
unique_hobbies.update(d['hobbies'])
print(unique_hobbies)

Categories