Python: update a nested dictionary [duplicate] - python

This question already has answers here:
How do I merge two dictionaries in a single expression in Python?
(43 answers)
Update value of a nested dictionary of varying depth
(28 answers)
Closed 6 months ago.
This post was edited and submitted for review 6 months ago and failed to reopen the post:
Original close reason(s) were not resolved
Let's say I am given a nested dicionary my_dict and another nested dict update: a sequence of keys (valid in my_dict) with a value to be assigned to the leaf at this sequence. For example,
my_dict = {
'city': 'NYC',
'person1': {'age': 25, 'name': 'Anna'},
'person2': {'age': 30, 'name': 'Bob'}
}
update0 = {'city': 'London'}
update1 = {'person1': {'age': 24}}
update2 = {'person2': {'age': 31}}
I need a function UpdateDict such that UpdateDict(my_dict, update1) will return
{
'city': 'NYC',
'person1': {'age': 24, 'name': 'Anna'},
'person2': {'age': 30, 'name': 'Bob'}
}
and UpdateDict(my_dict, update0) will return
{
'city': 'London',
'person1': {'age': 25, 'name': 'Anna'},
'person2': {'age': 30, 'name': 'Bob'}
}
In c++ I would have used explicit pointers/references to subdicts, but in python I'm not sure what to do, given that I don't know in advance the depth of the second argument (update one).
EDIT: if I try {**my_dict, **update1} or my_dict.update(update1) I get
{
'city': 'NYC',
'person1': {'age': 24},
'person2': {'age': 30, 'name': 'Bob'}
}
which is not desired: it does not change the value at a particular leaf only, but instead removes all the keys at the same level.
EDIT2: if someone needs this, I've solved it as follows
def UpdateDict(original, param):
for key in param.keys():
if type(param[key]) == dict:
UpdateDict(original[key], param[key])
else:
original[key] = param[key]

Related

How can i compare and remove nested dictionaries with the same values within the same dictionary?

If I have a dictionary with data in it like below what process should i enact like an if statement to delete duplicate entries such as nested dictionary 1 and 4. Lets say i wanted to delete 4 because the user entered it and i'm assuming that people are unique so they can't have the same demographics there can't be two John R. Smiths.
people = {1: {'name': 'John R. Smith', 'age': '27', 'sex': 'Male'},
2: {'name': 'Marie', 'age': '22', 'sex': 'Female'}
3: {'name': 'Mariah', 'age': '32', 'sex': 'Female'},
4: {'name': 'John R. Smith', 'age': '27', 'sex': 'Male'}}
I am just learning so i wouldn't be surprised if there is something simple I was unable to come up with.
I attempted to compare the entries such as if ['1']['name'] and ['1']['sex'] == ['4']['name'] and ['4']['sex']:
then print['4'] just to test and the error message told me that I need to be using indexes.
I've also turned it into a list which was successfull but was met with another error when trying to compare them in a manner like if person['name'] and person['age'] and person['sex'] is equal to another row within a four loop than print a message and i got nowhere.
I've also tried to turn it into a dataframe and use pandas duplicate function to remove the duplicates in which I got some error
yesterday about 'dict' probably because the dictionaries get nested in the dataframe contrasting to a list with nested
dictionaries which tends to look like this:
[{1: {'name': 'John', 'age': '27', 'sex': 'Male'},
2: {'name': 'Marie', 'age': '22', 'sex': 'Female'}]
You can take advantage of the fact that dict keys are always unique to help de-duplicate. Since dicts are unhashable and can't be used as keys directly, you can convert each sub-dict to a tuple of items first. Use dict.setdefault to keep only the first value for each distinct key:
records = {}
for number, record in people.items():
records.setdefault(tuple(record.items()), (number, record))
print(dict(records.values()))
Given your sample input, this outputs:
{1: {'name': 'John R. Smith', 'age': '27', 'sex': 'Male'}, 2: {'name': 'Marie', 'age': '22', 'sex': 'Female'}, 3: {'name': 'Mariah', 'age': '32', 'sex': 'Female'}}
Demo: https://replit.com/#blhsing/LonelyNumbWatch
One approach is to build a new dictionary by iterating over people and assigning a person to the new dictionary if their data is unique. The following solution uses a set for tracking unique users:
from pprint import pprint
unique_people = {}
unique_ids = set()
for key, data in people.items():
data_id = tuple(data.values())
if data_id in unique_ids:
continue
unique_people[key] = data
unique_ids.add(data_id)
pprint(unique_people)
Output:
{1: {'age': '27', 'name': 'John R. Smith', 'sex': 'Male'},
2: {'age': '22', 'name': 'Marie', 'sex': 'Female'},
3: {'age': '32', 'name': 'Mariah', 'sex': 'Female'}}

Create a list of dictionaries without overwriting [duplicate]

This question already has answers here:
List of lists changes reflected across sublists unexpectedly
(17 answers)
How do I clone a list so that it doesn't change unexpectedly after assignment?
(24 answers)
Closed 3 years ago.
I am looking for a way to update list listOfuser with dict User without over writing any last index.
[{'name': 'john', 'fimily': 'johny', 'studentId': '2120', 'age': 23},
{'name': 'mick', 'fimily': 'micky', 'studentId': '2121', 'age': 24}]
Result which I currently have:
[{'name': 'mick', 'fimily': 'micky', 'studentId': '2121', 'age': 24},
{'name': 'mick', 'fimily': 'micky', 'studentId': '2121', 'age': 24}]
Code snippet used to produce the said result:
UserInfo = {}
listOfUser = []
UserInfo['name'] = "john"
UserInfo['fimily'] = 'johny'
UserInfo['studentId'] = '2120'
UserInfo['age'] = 23
listOfUser.append(UserInfo)
print(listOfUser)
UserInfo['name'] = "mick"
UserInfo['fimily'] = 'micky'
UserInfo['studentId'] = '2121'
UserInfo['age'] = 24
listOfUser.append(UserInfo)
print(listOfUser)
is there any way ?
You need to append a copy of the dictionary to the list
listOfUser.append(UserInfo.copy())
output
[{'name': 'john', 'fimily': 'johny', 'studentId': '2120', 'age': 23},
{'name': 'mick', 'fimily': 'micky', 'studentId': '2121', 'age': 24}]

Why do the values in the dictionaries in the list cascade update after changing the value by index [duplicate]

This question already has answers here:
Understanding dict.copy() - shallow or deep?
(7 answers)
Closed 3 years ago.
I am changing value in the dictionary on the list by index. But all the similar dictionaries in the list also update this key's value.
My code is.
list=[]
dict = {'id':2,'name':'hanna', 'age':30}
list.append(dict)
list.append(dict)
list.append(dict)
list.append(dict)
list.append(dict)
list
Result
[{'age': 30, 'id': 2, 'name': 'hanna'},
{'age': 30, 'id': 2, 'name': 'hanna'},
{'age': 30, 'id': 2, 'name': 'hanna'},
{'age': 30, 'id': 2, 'name': 'hanna'},
{'age': 30, 'id': 2, 'name': 'hanna'}]
I want to change only this one
list[0]['age']='99'
but this update all the dictionaries in the list also..
[{'age': '99', 'id': 2, 'name': 'hanna'},
{'age': '99', 'id': 2, 'name': 'hanna'},
{'age': '99', 'id': 2, 'name': 'hanna'},
{'age': '99', 'id': 2, 'name': 'hanna'},
{'age': '99', 'id': 2, 'name': 'hanna'}]
I need this
[{'age': 99, 'id': 2, 'name': 'hanna'},
{'age': 30, 'id': 2, 'name': 'hanna'},
{'age': 30, 'id': 2, 'name': 'hanna'},
{'age': 30, 'id': 2, 'name': 'hanna'},
{'age': 30, 'id': 2, 'name': 'hanna'}]
Why? And how change the value in the dict by index?
Even though this will be closed, I can understand that for a beginner the linked duplicate can be really confusing.
I just wanted to add a simpler explanation in the hope of helping you.
Basically, when you append to your list, it is the same object everytime.
Here is the proof,
list=[]
dict = {'id':2,'name':'hanna', 'age':30}
list.append(dict)
list.append(dict)
print(id(list[0]))
print(id(list[1]))
Example of output:
>>> 11301752
11301752
So, when you're mutating the value after, you're actually doing it on the same object reference.
If you want more explanations on to how to overcome your problem definitely follow the duplicate link, it has a lot more information for you.
Python passes arguments "by object", which means that what the called function gets is not a copy but the original object itself - IOW, your list contains n references to the very same dict instance, so however you access it (via the name dict or via list[n]), it's still the very same dict you're updating. Note that this works the same for bindings, ie foo = {}; bar = foo will not create a copy of foo but make both names point to the same dict. All this is very clearly explained here
The cure is very obvious: make copies of the dict:
mydict = {"a":1}
mylist = []
mylist.append(mydict.copy())
mylist.append(mydict.copy())
mylist[1]["a"] = 42
print(mylist)
print(mydict)
Also note that dict.copy() returns a shallow copy. If your dict contains lists, other dicts or any mutable object, you may want a deepcopy instead.

Find a value in a list of dicts [duplicate]

This question already has answers here:
Get the first item from an iterable that matches a condition
(18 answers)
Closed 4 years ago.
Let:
M = [{'name': 'john', 'result': 12},
{'name': 'sara', 'result': 20},
{'name': 'karl', 'result': 11}]
If I want to find Sara's result, I thought about:
M[[m['name'] for m in M].index('sara')]['result'] # hard to read, but works
and
[m['result'] for m in M if m['name'] == 'sara'][0] # better
Is there an even more natural way to do this in Python?
Use a generator with next().
next(m['result'] for m in L if m['name'] == 'sara')
If you have several lookups to perform, linear search isn't the best option.
Rebuild a dictionary once with the proper key (name):
M = [{'name': 'john', 'result': 12},
{'name': 'sara', 'result': 20},
{'name': 'karl', 'result': 11}]
newdict = {d["name"] : d["result"] for d in M}
It creates:
{'john': 12, 'karl': 11, 'sara': 20}
now when you need the result from a name, just do:
print(newdict.get('sara'))

Converting a dict of dicts into a spreadsheet

My data is a dict of dicts like this (but with a lot more fields and names):
{'Jack': {'age': 15, 'location': 'UK'},
'Jill': {'age': 23, 'location': 'US'}}
I want to export it to a spreadsheet like this:
Name Age Location
Jack 15 UK
Jill 23 US
But apparently csv.DictWriter wants to see a list of dicts like this:
[{'name': 'Jack', 'age': 15, 'location': 'UK'},
{'name': 'Jill', 'age': 23, 'location': 'US'}]
What's the simplest way to get my dict of dicts into a CSV file?
I suppose the entries should be in alphabetical name order, too, but that's easy enough to do in the spreadsheet software.
mydict = {'Jack': {'age': 15, 'location': 'UK'},
'Jill': {'age': 23, 'location': 'US'}}
datalist = []
for name in mydict:
data = mydict[name]
data['name'] = name
datalist.append(data)
And datalist will hold the desired result. Notes:
This also modifies mydict (adding the 'name' key to each datum). You probably don't mind about that. It's a bit more efficient than the alternative (copying).
It's not the most flashy one-line-way-to-do-it, but it's very readable, which IMHO is more important
You can use list-comprehension to get your new list of dicts:
>>> [dict(zip(['name'] + attrs.keys(), [name] + attrs.values())) \
for name, attrs in d.iteritems()]
[{'age': 23, 'location': 'US', 'name': 'Jill'},
{'age': 15, 'location': 'UK', 'name': 'Jack'}]
EDIT: d is your dict:
>>> d
{'Jack': {'age': 15, 'location': 'UK'}, 'Jill': {'age': 23, 'location': 'US'}}

Categories