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'))
Related
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]
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})
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?
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.
I have a list of items with properties "Type" and "Time" that I want to quickly sum the time for each "Type" and append to another list. The list looks like this:
Items = [{'Name': A, 'Type': 'Run', 'Time': 5},
{'Name': B, 'Type': 'Walk', 'Time': 15},
{'Name': C, 'Type': 'Drive', 'Time': 2},
{'Name': D, 'Type': 'Walk', 'Time': 17},
{'Name': E, 'Type': 'Run', 'Time': 5}]
I want to do something that works like this:
Travel_Times=[("Time_Running","Time_Walking","Time_Driving")]
Run=0
Walk=0
Drive=0
for I in Items:
if I['Type'] == 'Run':
Run=Run+I['Time']
elif I['Type'] == 'Walk':
Walk=Walk+I['Time']
elif I['Type'] == 'Drive':
Drive=Drive+I['Time']
Travel_Times.append((Run,Walk,Drive))
With Travel_Times finally looking like this:
print(Travel_Times)
[("Time_Running","Time_Walking","Time_Driving")
(10,32,2)]
This seems like something that should be easy to do efficiently with either a list comprehension or something similar to collections.Counter, but I can't figure it out. The best way I have figured is to use a separate list comprehension for each "Type" but that requires iterating through the list repeatedly. I would appreciate any ideas on how to speed it up.
Thanks
Note that case is very important in Python :
For isn't a valid statement
Travel_times isn't the same as Travel_Times
there's no : after elif
Travel_Times.append(... has a leading space, which confuses Python
items has one [ too many
A isn't defined
Having said that, a Counter works just fine for your example :
from collections import Counter
time_counter = Counter()
items = [{'Name': 'A', 'Type': 'Run', 'Time': 5},
{'Name': 'B', 'Type': 'Walk', 'Time': 15},
{'Name': 'C', 'Type': 'Drive', 'Time': 2},
{'Name': 'D', 'Type': 'Walk', 'Time': 17},
{'Name': 'E', 'Type': 'Run', 'Time': 5}]
for item in items:
time_counter[item['Type']] += item['Time']
print(time_counter)
# Counter({'Walk': 32, 'Run': 10, 'Drive': 2})
To get a list of tuples :
[tuple(time_counter.keys()), tuple(time_counter.values())]
# [('Run', 'Drive', 'Walk'), (10, 2, 32)]
You can use a dict to keep track of the total times. Using the .get() method, you can tally up the total times. If the key for the activity doesn't already exist, set its tally to zero and count up from there.
items = [{'Name': 'A', 'Type': 'Run', 'Time': 5},
{'Name': 'B', 'Type': 'Walk', 'Time': 15},
{'Name': 'C', 'Type': 'Drive', 'Time': 2},
{'Name': 'D', 'Type': 'Walk', 'Time': 17},
{'Name': 'E', 'Type': 'Run', 'Time': 5}]
totals = {}
for item in items:
totals[item['Type']] = totals.get(item['Type'], 0) + item['Time']
for k, v in totals.items():
print("Time {}ing:\t {} mins".format(k, v))
You could use Counter from collections along with chain and repeat from itertools:
from itertools import chain, repeat
from collections import Counter
from_it = chain.from_iterable
res = Counter(from_it(repeat(d['Type'], d['Time']) for d in Items))
This small snippet results in a Counter instance containing the sums:
print(res)
Counter({'Drive': 2, 'Run': 10, 'Walk': 32})
It uses repeat to, obviously, repeat the d['Type'] for d['Time'] times and then feeds all these to Counter for the summation using chain.from_iterable.
If your Items list has many entries, you can again use chain.from_iterable to chain these all together:
res = Counter(from_it(repeat(d['Type'], d['Time']) for d in from_it(Items)))
This will get you a sum of all types in all the nested lists.
You can use reduce with collections.Counter:
# from functools import reduce # Python 3
d = reduce(lambda x, y: x + Counter({y['Type']: y['Time']}), Items, Counter())
print(d)
# Counter({'Walk': 32, 'Run': 10, 'Drive': 2})
It simply builds up the Counter updating each Type using the corresponding Time value.
Here is a brief way of expressing what you'd like in one line. By the way, your list Items doesn't need to be double bracketed:
>>> Items = [{'Type': 'Run', 'Name': 'A', 'Time': 5},
{'Type': 'Walk', 'Name': 'B', 'Time': 15},
{'Type': 'Drive', 'Name': 'C', 'Time': 2},
{'Type': 'Walk', 'Name': 'D', 'Time': 17},
{'Type': 'Run', 'Name': 'E', 'Time': 5}]
>>> zip(("Time_Running","Time_Walking","Time_Driving"), (sum(d['Time'] for d in Items if d['Type'] == atype) for atype in 'Run Walk Drive'.split()))
[('Time_Running', 10), ('Time_Walking', 32), ('Time_Driving', 2)]
Here I zipped your output labels to a generator that calculates the sum for each of the three transportation types you have listed. For your exact output you could just use:
>>> [("Time_Running","Time_Walking","Time_Driving"), tuple(sum(d['Time'] for d in Items if d['Type'] == atype) for atype in 'Run Walk Drive'.split())]
[('Time_Running', 'Time_Walking', 'Time_Driving'), (10, 32, 2)]
If you're willing to abuse generators for their side effects:
from collections import Counter
count = Counter()
# throw away the resulting elements, as .update does the work for us
[_ for _ in (count.update({item['Type']:item['Time']}) for item in items) if _]
>>> count
Counter({'Walk': 32, 'Run': 10, 'Drive': 2})
This works because Counter.update() returns None. if None will always evaluate False and throw out that element. So this generates a side effect empty list [] as the only memory overhead. if False would work equally well.
Just use a dictionary! Note that in python it is idomatic to use snake_case for variables and keys.
travel_times = {'run': 0, 'walk': 0, 'drive': 0}
for item in items:
action, time = item['type'], item['time']
travel_times[action] += time