I have a dictionary of dictionary called data_dict. Following is how it looks:
{'UMANOFF ADAM S': {'total_stock_value': 'NaN', 'loans': 'NaN', 'salary': 288589},
'YEAP SOON': {'total_stock_value': 192758, 'loans': 'NaN', 'salary': 'NaN'},
'PIPER GREGORY F': {'total_stock_value': 880290, 'loans': 1452356, 'salary': 19791},
'Jack S': {'total_stock_value': 88000, 'loans': 'NaN', 'salary': 288589}
}
Basically it is of the format
{Person Name : Dictionary of that person's attributes}
I am trying to find the name of a person whose salary is certain X.
Specifically in above example - let's say I am trying to find the name of the persons whose salary is 288589. I expect all the names whose salary is 288589.
I have written following generalised function which will take a search key and value and return names of the persons for which that key, value holds true.
def search_person_by_attribute(attribute, value):
person_names = []
for person, attributes_dict in data_dict.items():
if attributes_dict[attribute] == value:
person_names.append(person)
return person_names
This method runs successfully
results = search_person_by_attribute("salary", 288589)
print(results)
and prints
['UMANOFF ADAM S','Jack S']
But somehow I feel this is quite a long way write it. Is there a better/shorter/more pythonic way to do it?
If you can also mention the efficiency (in terms of time complexity) of my as well your suggested solution will be a great bonus.
I would suggest something like this, which I think is not just shorter, but more readable than your version:
def search_person_by_attribute(d, attribute, value):
return [name for name in d if d[name][attribute] == value]
It works exactly like yours, but requires the dictionary as an additional parameter, because I think that's better style:
>>> search_person_by_attribute(d, "salary", 288589)
['UMANOFF ADAM S', 'Jack S']
Related
I am trying to work out how to complete a task that asks the following:
Create a string and use a method to generate a description of the new car as shown below. Print it – it should be formatted like the text below (including the line breaks).
Yesterday I bought a [Make] [Model].
It’s not “too” old. It was made in [year] …
I like it, though. It’s [colour], and it has [doors] doors!
The variables (make, model, year, colour and doors) need to be populated from a dictionary.
I have programmed the dictionary to look like this:
Car = {
'Make': 'Mitsubishi',
'Model': 'Lancer',
'Year': '2002',
'Colour': 'Green',
'Doors': '4',
}
How can i fill the variables in that string of text by referencing the dictionary 'Car'?
Thank you!
You can create a template string, and then burst the dictionary values to format it. Example.
fstr = "Yesterday I bought a {} {}.\n\nIt’s not “too” old. It was made in {} …\n\nI like it, though. It’s {}, and it has {} doors!"
Car = {
'Make': 'Mitsubishi',
'Model': 'Lancer',
'Year': '2002',
'Colour': 'Green',
'Doors': '4',
}
print(fstr.format(*Car.values()))
Gives an output like
Yesterday I bought a Mitsubishi Lancer.
It’s not “too” old. It was made in 2002 …
I like it, though. It’s Green, and it has 4 doors!
So, you can apply the format with any dictionary you want.
Condition: You have to make sure the key/values are in the same order of the fillers.
python uses f-strings for this. You can use f-strings to pass variables to your string, while still keep it readable.
output = f"Yesterday I bought a {Car['Make']} {Car['Model']}."
you use \n to represent a newline, and \ to escape any "" in the string itself:
output = f"Yesterday I bought a {Car['Make']} {Car['Model']}.\nIt’s not \“too\” old. It was made in {Car['Year']}"
extra: Pythons PEP8 style guides mention that variable names (like your dictionary) should always start with a lowercase.
Edit: Thanks to Timus for pointing out a small error. added '' around dict keys.
Build a string with the dictionary keys as placeholders (included in curly braces), and then use .format_map() with the dictionary as argument on it:
string = '''Yesterday I bought a {Make} {Model}.
It’s not “too” old. It was made in {Year} …
I like it, though. It’s {Colour}, and it has {Doors} doors!'''
Car = {
'Make': 'Mitsubishi',
'Model': 'Lancer',
'Year': 2002,
'Colour': 'Green',
'Doors': 4,
}
print(string.format_map(Car))
(The ordering of the dictionary is irrelvant.)
This question already has answers here:
find and update a value of a dictionary in list of dictionaries
(2 answers)
Closed 1 year ago.
I am trying to update the department of bob to Human Resources. I was under the impression from my readings and research that you just needed to rename the value within the key as I have done within my code but I keep getting an Assertion error. Any tips?
directory= [{'firstName': "bob", 'department': "Accounting", 'salary': 50000}, {'firstName': "alice", 'department': "Marketing", 'salary': 100000}]
#My Code
directory['department'] = 'Human Resources'
Do this:
directory= [{'firstName':"bob",'department':"Accounting",'salary':50000},{'firstName':"alice",'department':"Marketing",'salary':100000}]
for d in directory:
if 'firstName' in d and d['firstName'] == 'bob':
d['department'] = 'Human Resources'
break
print(directory) # [{'firstName': 'bob', 'salary': 50000, 'department': 'Human Resources'}, {'firstName': 'alice', 'salary': 100000, 'department': 'Marketing'}]
Your problem is that you're not actually dealing with a dictionary, but a list of dictionaries. This means you can't directly access the key, you have to access the item in the list.
To fix your current code:
directory[0]['department'] = 'Human resources'
Notice the [0].
You can tell this is the data structure because it has square brackets before and after the curly brackets. Eg. directory =[{ ... }]
For future reference, if you would like to look up someone by name, you'll need to iterate through the list and then update the appropriate dictionary.
for entry in directory:
# We're looking for Bob from accounting, not Bob the auditor.
if entry['name'] == 'bob' and entry['department'] == 'Accounting':
entry['department'] = 'Human Resources'
# break makes sure that this only happens once.
break
Since bob is a relatively common name, you might need to make your name values more unique. Bob Vila is more unique than bob, meaning you are less likely to address someone you don't mean to.
Of course, if you know that you have a unique value for each entry in the directory, then you can convert the whole thing into a dict.
# This is a dictionary comprehension. They're pretty neat.
new_directory = {employee['name']: employee for employee in directory}
new_directorr['bob']['department'] = 'Human resources'
On the linked question, the recommendation is a generator expression inside of next:
try:
bob = next(employee for employee in directory if employee['name'] == 'bob')
bob['department'] = 'Human Resources'
except StopIteration: # next will cause a exception if bob cannot be found.
pass
But you should note: the use of next with a list comprehension as well as for...in the iteration above: both are good ideas in some circumstances, but they will slow down over time as your directory gets bigger.
This is list of dictionary.
It is basically a sample data, but there are are more items in the list.
I want to basically get the dictionary using a value of the dictionary.
[{'status_id': '153080620724_10157915294545725', 'status_message': 'Beautiful evening in Wisconsin- THANK YOU for your incredible support tonight! Everyone get out on November 8th - and VOTE! LETS MAKE AMERICA GREAT AGAIN! -DJT', 'link_name': 'Timeline Photos', 'status_type': 'photo', 'status_link': 'https://www.facebook.com/DonaldTrump/photos/a.488852220724.393301.153080620724/10157915294545725/?type=3', 'status_published': '10/17/2016 20:56:51', 'num_reactions': '6813', 'num_comments': '543', 'num_shares': '359', 'num_likes': '6178', 'num_loves': '572', 'num_wows': '39', 'num_hahas': '17', 'num_sads': '0', 'num_angrys': '7'}
{'status_id': '153080620724_10157914483265725', 'status_message': "The State Department's quid pro quo scheme proves how CORRUPT our system is. Attempting to protect Crooked Hillary, NOT our American service members or national security information, is absolutely DISGRACEFUL. The American people deserve so much better. On November 8th, we will END this RIGGED system once and for all!", 'link_name': '', 'status_type': 'video', 'status_link': 'https://www.facebook.com/DonaldTrump/videos/10157914483265725/', 'status_published': '10/17/2016 18:00:41', 'num_reactions': '33768', 'num_comments': '3644', 'num_shares': '17653', 'num_likes': '26649', 'num_loves': '487', 'num_wows': '1155', 'num_hahas': '75', 'num_sads': '191', 'num_angrys': '5211'}
{'status_id': '153080620724_10157913199155725', 'status_message': "Crooked Hillary's State Department colluded with the FBI and the DOJ in a DISGRACEFUL quid pro quo exchange where her staff promised FBI agents more overseas positions if the FBI would alter emails that were classified. This is COLLUSION at its core and Crooked Hillary's super PAC, the media, is doing EVERYTHING they can to cover it up. It's a RIGGED system and we MUST not let her get away with this -- our country deserves better! Vote on Nov. 8 and let's take back the White House FOR the people and BY the people! #AmericaFirst! #RIGGED http://www.politico.com/story/2016/10/fbi-state-department-clinton-email-229880", 'link_name': '', 'status_type': 'video', 'status_link': 'https://www.facebook.com/DonaldTrump/videos/10157913199155725/', 'status_published': '10/17/2016 15:34:46', 'num_reactions': '85627', 'num_comments': '8810', 'num_shares': '32594', 'num_likes': '73519', 'num_loves': '2943', 'num_wows': '1020', 'num_hahas': '330', 'num_sads': '263', 'num_angrys': '7552'}
{'status_id': '153080620724_10157912962325725', 'status_message': 'JournoCash: Media gives $382,000 to Clinton, $14,000 Trump, 27-1 margin:', 'link_name': 'JournoCash: Media gives $382,000 to Clinton, $14,000 Trump, 27-1 margin', 'status_type': 'link', 'status_link': 'http://www.washingtonexaminer.com/journocash-media-gives-382000-to-clinton-14000-trump-27-1-margin/article/2604736', 'status_published': '10/17/2016 14:17:24', 'num_reactions': '22696', 'num_comments': '3665', 'num_shares': '5082', 'num_likes': '14029', 'num_loves': '122', 'num_wows': '2091', 'num_hahas': '241', 'num_sads': '286', 'num_angrys': '5927'}
]
I want the value for the highest number of 'num_likes' and print the status_id for that particular dictionary which has the highest 'num_likes'. I also want to understand how the method or process to implement this. I basically use the list to obtain the values and then find the maximum, is there any other way to do it?
The output should be just status_id.
Here I'm declaring your list-of-dictionaries as variable list_of_objs.
Since the num_likes value is string-type using int(obj['num_likes']) to convert the string-to-int - passing that to max method will return what is th max_likes .
list_of_objs = [{..}, {..}, {..}]
max_likes = max([int(obj['num_likes']) for obj in list_of_objs if 'num_likes' in obj.keys()])
print(max_likes)
max_likes_objs =[obj for obj in list_of_objs if int(obj['num_likes'])==max_likes]
print(max_likes_objs)
Last line what I've printed is list of all the dictionaries that have the max-value of num-likes
You can try this:
k=max([i['num_likes'] for i in d])
[i['status_id'] for i in d if i['num_likes']==k][0]
Using a simpler list as example:
l = [
{'likes': 5, 'id': 1},
{'likes': 2, 'id': 2},
{'likes': 7, 'id': 3},
{'likes': 1, 'id': 4},
]
result = list(filter(lambda item: item['likes'] == max([item['likes'] for item in l]), l))
print(result)
this will print [{'likes': 7, 'id': 3}]. The problem here is that if you can have more than one "maximum like item". This is why the function return a list. To print all of the IDs you can to:
print([item['id'] for item in result])
If you are sure that there are no more than one item or, otherwise, you need exactly one (maybe the first) you can do:
result = list(filter(lambda item: item['likes'] == max([item['likes'] for item in l]), l))
result = result[0]['id']
print(result)
which will print 3 in the example.
Now how to approach this problem: first you need the maximum number of likes:
max([item['likes'] for item in l])
call it maxLikes. Then you need the to take all the items with this likes value:
filter(lambda item: item['likes'] == maxLikes, l)
this is a filter applied on the list l (the last argument on the right), with a lambda function that could be read as "all items with 'likes' property equal to the maxLikes number".
Then you transform this in a list with list.
Declaring list_of_status_ids = [{}, {} ...]
Iterate list_of_status_ids and add in a dict having key as num_likes and values as list of status_id.
Then take max of num_likes and get all status_id corresponding to that max num_likes.
from collections import defaultdict
status_id_map = defaultdict(list)
[status_id_map[obj['num_likes']].append(obj['status_id']) for obj in list_of_status_ids]
print status_id_map.get(max(status_id_map.keys()))
I am new to Python, and I want to know if there is a way to make the age value calculated using the year of birth which is an item with the age in the same dictionary.
This is what it came to my mind, and I think there is simple way like this without using additional variables of functions.
person = {
'name': Jane,
'yearofbirth': 1995,
'yearnow': 2019,
'age': person['yearnow'] + person['yearofbirth']
}
Any help would be appreciated. Thank you!
Yes, you can
Just not decalre the whole dict in one act
person = {
'name': Jane,
'yearofbirth': 1995,
'yearnow': 2019
}
person["age"] = (lambda yearnow, yearofbirth: yearnow - yearofbirth)(**person)
But in your example you shouldn't change anything, because there is no way to simplify it(easily). My solution should be used only in complicated tasks. I just you the way to simplify it in case of a huge amount of values in dict.
Instead of hardcoding the current year, you could get python to give it to you
from datetime import datetime
currentYear = datetime.now().year
person = {
'name': 'Jane',
'yearofbirth' : 1995
}
age = currentYear - person.get( "yearofbirth", "")
person.update({'age': age})
print(person)
You can't set your age inside the dict, as it has not been defined yet.
If you do like above code, we are setting the age outside the dict, then updating the dict with the value we just calculated based on currentYear and the age
The output is:
{'name': 'Jane', 'yearofbirth': 1991, 'age': 24}
You may have self referential dictionary by using a class derived from dict but dict itself doesn't have this capability. The following code is taken from this answer.
class MyDict(dict):
def __getitem__(self, item):
return dict.__getitem__(self, item) % self
dictionary = MyDict({
'user' : 'gnucom',
'home' : '/home/%(user)s',
'bin' : '%(home)s/bin'
})
print dictionary["home"]
print dictionary["bin"]
Lets say we have a list of dictionaries, ranging in the thousands. Every dictionary has the exact same keys, but different values.
Is there any way to lookup which dictionary a key value comes from, and then access different key values from that same dictionary?
For example, say you have a list containing every person in a large city, dictionary format, like so:
people_in_city = [
{'name': 'Bob Jang',
'age': 45,
'sex': 'male'},
{'name': 'Barb Esau',
'age': 56,
'sex': 'female'},
etc.
etc.
Now, you want to lookup the age of Bob Jang, but you only have his name. Is there anyway to get his corresponding age using this format?
There's no fast way to do this in Python. I'd suggest something like:
def get_dict_from_key(list_of_dicts, key, value):
return next((d for d in list_of_dicts if d[key] == value))
result_d = get_dict_from_key(dicts, 'name', 'Bob Jang')
result_d['age']
That said, this is the kind of thing that relational databases are made for! Learn SQL and use it :)
I would suggest looking at a database as Adam Smith suggested. Though I would also like to suggest looking at a class. That would go something like:
class Person():
def __init__(self, name, age, gender):
self.name = name
self.age = age
self.gender = gender
people_in_city = []
def add_person(name, age, gender
people_in_city=people_in_city):
people_in_city.append(Person(name, age, gender))
def find_by_name(name):
for person in people_in_city:
if person.name == name:
return person
Not the most elegant way, but it gets the job done, plus you can add more information and not have have to change the nature of your search function. So lets say you find dear 'Bob Jang', and you realize that you want to know that job he is doing (assuming you coded that into the class). You just do:
person_of_interest = find_by_name('Bob Jang')
person_of_interest.job
person_of_interest.age
Note that this only gives the LAST found value of the name, and not everyone with that name. Other methods will have to be employed for that. This method also means that you are holding all the information in a list, and that might get slow as the list grows. That is why databases would work better as your list grows.
And as a bonus, it is possible to create each person in parallel.
Try this,
provided_name = 'some name'
persons_list_with_name = [person_info in person_info in people_in_city if person_info['name'] == provided_name]
for person_info in persons_list_with_name:
print person_info['name'], person_info['age']
(age,) = [v['age' ]for (k,v) in people_in_city.iteritems() if v['name']=="Bob Jang"]
people_in_city = [
{'name': 'Bob Jang',
'age': 45,
'sex': 'male'},
{'name': 'Barb Esau',
'age': 56,
'sex': 'female'}]
v = age or name sex etc..
for i in people_in_city:
if v in i.values():
print i
There is a python package called bidict that can do this. It provides a two-way dict, which allows you to get the key from the value or the value from the key. An example from the documentation:
>>> element_by_symbol = bidict(H='hydrogen')
>>> element_by_symbol['H'] # forward mapping works just like with dict
'hydrogen'
>>> element_by_symbol[:'hydrogen'] # use slice for the inverse mapping
'H'