Find lowest value in a list of dictionaries in python - python

I want to find and return the minimal value of an id in a string, for example:
find_min_id([{"nonid": "-222", "id": 0}, {"id": -101}])
-101
find_min_id([{’id’: 63, 'id': 42}])
42
So far I have this:
def find_min_id(list):
return min(list)
but that gives:
{'id': -101}
and I only want the value of the lowest id.

Use the key parameter of min:
def find_min_id(l):
return min(l, key=lambda d: d.get("id", float('inf')))["id"]
This actually finds the min id, and does it without creating a new list.
The only thing is, the elements in your list might not had an 'id' key. For that reason I had to use .get("id", float('inf')). Thus the function will return inf if there is no id key, which might not be desirable. What min() does when given an empty list is it throws an exception, so we'd probably like to do the same if none of the dicts we pass it have an 'id' key. In that case the min of a generator appoach might indeed be better:
def find_min_id(l):
return min(d["id"] for d in l if "id" in d)
The other approach would be to check for inf as the result of min, but this is more cumbersome:
import math
def find_min_id(l):
res = min(l, key=lambda d: d.get("id", float('inf')))["id"]
if math.isinf(res):
raise ValueError("No dict has an 'id' key")
return res

Another approach, but works where there is no id in the dictionary, and when there is no dictionary with an id at all:
def find_min_id(lst):
ids = [d['id'] for d in lst if 'id' in d]
return min(ids) if ids else None
Without exceptions, and without running min for artificially extended list (i.e. the answer which puts maximum floats where an entry isn't an id-entry).

Following code return None if no id key is in list.
>>> data = [{"nonid": "-222", "id": 0}, {"id": -101}, {"nonid":-200}]
>>> min(filter(lambda x: x is not None, map(lambda x: x.get('id', None),
... data)) or [None])
-101
Here filter(lambda x: x is not None, ...) filters out dictionaries without id,
map(lambda x: x.get('id', None), data) gets all id's from data, and or [None] part treats case when no id key will be found in data.

You are trying to find the 'lowest' dictionaries. What we want is, to find the lowest 'id' value in the list.
def find_min_id(lst):
return min([d[key] for key in d for d in lst if key=="id"])
Also, avoid using list as a variable name, it overrides the built-in function list().
A little demo:
>>> def find_min_id(lst):
return min([d[key] for key in d for d in lst if key=="id"])
>>> find_min_id(lst)
-101
Hope this helps!

>>> ids = [{"nonid": "-222", "id": 0}, {"id": -101}]
>>> min([val for obj in ids for key, val in obj.items() if key == 'id'])
-101
>>> ids = [{'id': 63}, { 'id': 42}]
>>> min([val for obj in ids for key, val in obj.items() if key == 'id'])
42
Try the above.
You can make this into a function definition:
def find_lowest(ids):
return min([val for obj in ids for key, val in obj.items() if key == 'id'])
Working example.
Let me explain what I'm doing. Firstly, the min function takes in an iterable object as an argument. So, let me demonstrate:
>>> min([1,2,3,4,6,1,0])
0
So, what this means is this, we are essentially taking the minimum value of the list that we get from this, [val for obj in ids for key, val in obj.items() if key == 'id'].
Now, you might be wondering, well whats happening in there? It might be a little intimidating at first, but thats a list comprehension. Whats that you say? Well, in simple terms its a concise we in which we make a list:
Let me start with be first part, and no its not the beginning of the statement:
for obj in ids
What we are doing here, is iterating over all the dictionary objects in in side of ids. Now, we use that object here:
key, val in obj.items() if key == 'id'
Since object, is a dict, we use the items function to get a generator that gives a tuple of key, value pairs. In an object like this: {'id': 100}, the id would be they key and 100 would be the value. So, we are going over all the items in the dictionary object, and if the key happens to be id, then we append it to the list:
[val
Thats what the first part does. The first part of the list comprehension appends something to the final list, and that is val.
UPDATE:
If for some reason, the list does not containt anything with id as a key, then it will throw a ValueError as min does not accept an empty list, so to remedy this, we can check:
def find_lowest(ids):
_ret = [val for obj in ids for key, val in obj.items() if key == 'id']
if _ret:
return min(_ret)
else:
return None

list is a built-in type in Python. don't use it as an identifier
def find_min_id(my_list)
id_list = []
for record in my_list:
if 'id' in record:
id_list.append(record['id'])
return min(id_list)

Related

Python>Finding key based on values within a list, from a dictionary

i am using a dict like below and i want a function preferably which returns the key value, based on the value user provides.
test_dict={'Name':[abc,def],'Address':['Local','NonLocal']}
Used the below function:
def find_key_for(input_dict,value):
result=[]
for k,v in input_dict.items():
if value in v:
result.append(k)
return result
when i use the above function as below:
print(find_key_for(test-dict,'abc')) it returns []
while i expected Name
alternatively when i do (find_key_for(test-dict,'Local')) it returns [Address] which is expected.
why does it not work for Name? what changes can i make?
You need to un-indent the return statement:
def find_key_for(input_dict, value):
result = []
for k, v in input_dict.items():
if value in v:
result.append(k)
return result
The reason is because if you don't, and have it inside the for loop, your function will return after only looping once.
Finally, you can use a nested list comprehension in just one line:
test_dict = {'Name': ['abc', 'def'], 'Address': ['Local', 'NonLocal']}
def find_key_for(input_dict, value):
return [k for k in input_dict if value in input_dict[k]]
print(find_key_for(test_dict, 'abc'))
Or a lambda function:
test_dict = {'Name': ['abc', 'def'], 'Address': ['Local', 'NonLocal']}
find_key_for = lambda input_dict, value: [k for k in input_dict if value in input_dict[k]]
print(find_key_for(test_dict, 'abc'))
Output:
['Name']
Firstly, please be sure to format your code properly so it's more legible. You can do this by clicking the code {} up at the top of the screen.
Also, this code works completely fine for me. One thing that I noticed that could be an issue (but could also be you mistyping it here) was that in the list for 'Name', you have 'abc' and 'def' written without the parantheses, and you could accidentally be assigning it to an object rather than a string. Also, you wrote "test_dict" as "test-dict" later on.

How do I iterate over entire dictionary keys?

Diction is a dictionary containing keys and values. I want to iterate over dictionary keys in which I will return an array of keys with values less than or equal to 20. But, I am only iterating over one key. How do I iterate over the entire dictionary keys?
def total(diction):
for key in diction:
if diction[key] <= 20:
return [key]
Your function finishes execution when it hits the first return statement.
You can adjust your function like this.
def total(diction):
result = []
for key, value in diction.items():
if value <= 20:
result.append(key)
return result
This function appends the keys satisfying your criterion to a list and only returns that list once it has looked at all (key, value) pairs in the dict.
Alternatively, you can write a generator function:
def total_gen(diction):
for key, value in diction.items():
if value <= 20:
yield key
You might consider giving the functions a better name than total and have them take an additional parameter (for example named limit) in order avoid hardcoding the value 20.
Demo:
>>> d = {'a': 5, 'b': 100, 'c': 23, 'd': -2}
>>> total(d)
>>> ['d', 'a']
>>> list(total_gen(d))
>>> ['d', 'a']
Of course, you could also use succinct list or generator expressions:
>>> [key for key, value in d.items() if value <= 20]
>>> ['d', 'a']
>>>
>>> for k in (key for key, value in d.items() if value <= 20):
...: print(k)
...:
d
a
The generator-function and the generator expression are especially useful in cases where you don't need all the keys in memory at once - for example if you just want to iterate over them.
You can iterate over the dict items with a list comprehension like this:
def total(diction):
return [key for key, value in diction.items() if value <= 20]
def total(diction):
result = []
for key in diction:
if diction[key] <= 20:
result.append(key)
return result
Or using a list comprehension
def total(diction):
return [key for key in diction if diction[key] <= 20]
You need to save the keys to a list, then only return after you have gone through all the keys.
def total(diction):
key_list = []
for key in diction:
if diction[key] <= 20:
key_list.append(key)
return key_list
This could also be done with list comprehension:
def total(diction):
return [k for k in diction if diction[k]<=20]
I am not totally sure if I understood your question correctly. But you can get a list of keys of a dictionary object with ".keys()" attribute of a dictionary. E.g. if you have a dictionary object named "diction" then with diction.keys() you get a list object with keys as list item in it. Then you can iterate through each keys like that:
For dict_key in diction.keys():
diction[dict_key] = ......
Hope, it helps
What do you mean by 'iterating over one key'? You are indeed iterating over all the keys when you write this, one at a time -
for key in diction:
If you want to do that in a single line, you could use a lambda function to shorten the code -
list(filter(lambda x: dic[x]<=20, dic.keys()))
Although, even here you check the keys one at a time. That's iteration!!

Merging values from 2 dictionaries (Python)

(I'm new to Python!)
Trying to figure out this homework question:
The function will takes a​s input​ two dictionaries, each mapping strings to integers. The function will r​eturn​ a dictionary that maps strings from the two input dictionaries to the sum of the integers in the two input dictionaries.
my idea was this:
def ​add(​dicA,dicB):
dicA = {}
dicB = {}
newdictionary = dicA.update(dicB)
however, that brings back None.
In the professor's example:
print(add({'alice':10, 'Bob':3, 'Carlie':1}, {'alice':5, 'Bob':100, 'Carlie':1}))
the output is:
{'alice':15, 'Bob':103, 'Carlie':2}
My issue really is that I don't understand how to add up the values from each dictionaries. I know that the '+' is not supported with dictionaries. I'm not looking for anyone to do my homework for me, but any suggestions would be very much appreciated!
From the documentation:
update([other])
Update the dictionary with the key/value pairs from other, overwriting existing keys. Return None.
You don't want to replace key/value pairs, you want to add the values for similar keys. Go through each dictionary and add each value to the relevant key:
def ​add(​dicA,dicB):
result = {}
for d in dicA, dicB:
for key in d:
result[key] = result.get(key, 0) + d[key]
return result
result.get(key, 0) will retrieve the value of an existing key or produce 0 if key is not yet present.
First of all, a.update(b) updates a in place, and returns None.
Secondly, a.update(b) wouldn't help you to sum the keys; it would just produce a dictionary with the resulting dictionary having all the key, value pairs from b:
>>> a = {'alice':10, 'Bob':3, 'Carlie':1}
>>> b = {'alice':5, 'Bob':100, 'Carlie':1}
>>> a.update(b)
>>> a
{'alice': 5, 'Carlie': 1, 'Bob': 100}
It'd be easiest to use collections.Counter to achieve the desired result. As a plus, it does support addition with +:
from collections import Counter
def add(dicA, dicB):
return dict(Counter(dicA) + Counter(dicB))
This produces the intended result:
>>> print(add({'alice':10, 'Bob':3, 'Carlie':1}, {'alice':5, 'Bob':100, 'Carlie':1}))
{'alice': 15, 'Carlie': 2, 'Bob': 103}
The following is not meant to be the most elegant solution, but to get a feeling on how to deal with dicts.
dictA = {'Alice':10, 'Bob':3, 'Carlie':1}
dictB = {'Alice':5, 'Bob':100, 'Carlie':1}
# how to iterate through a dictionary
for k,v in dictA.iteritems():
print k,v
# make a new dict to keep tally
newdict={}
for d in [dictA,dictB]: # go through a list that has your dictionaries
print d
for k,v in d.iteritems(): # go through each dictionary item
if not k in newdict.keys():
newdict[k]=v
else:
newdict[k]+=v
print newdict
Output:
Bob 3
Alice 10
Carlie 1
{'Bob': 3, 'Alice': 10, 'Carlie': 1}
{'Bob': 100, 'Alice': 5, 'Carlie': 1}
{'Bob': 103, 'Alice': 15, 'Carlie': 2}
def ​add(​dicA,dicB):
You define a function that takes two arguments, dicA and dicB.
dicA = {}
dicB = {}
Then you assign an empty dictionary to both those variables, overwriting the dictionaries you passed to the function.
newdictionary = dicA.update(dicB)
Then you update dicA with the values from dicB, and assign the result to newdictionary. dict.update always returns None though.
And finally, you don’t return anything from the function, so it does not give you any results.
In order to combine those dictionaries, you actually need to use the values that were passed to it. Since dict.update mutates the dictionary it is called on, this would change one of those passed dictionaries, which we generally do not want to do. So instead, we use an empty dictionary, and then copy the values from both dictionaries into it:
def add (dicA, dicB):
newDictionary = {}
newDictionary.update(dicA)
newDictionary.update(dicB)
return newDictionary
If you want the values to sum up automatically, then use a Counter instead of a normal dictionary:
from collections import Counter
def add (dicA, dicB):
newDictionary = Counter()
newDictionary.update(dicA)
newDictionary.update(dicB)
return newDictionary
I suspect your professor wants to achieve this using more simple methods. But you can achieve this very easily using collections.Counter.
from collections import Counter
def add(a, b):
return dict(Counter(a) + Counter(b))
Your professor probably wants something like this:
def add(a, b):
new_dict = copy of a
for each key/value pair in b
if key in new_dict
add value to value already present in new_dict
else
insert key/value pair into new_dict
return new_dict
You can try this:
def add(dict1, dict2):
return dict([(key,dict1[key]+dict2[key]) for key in dict1.keys()])
I personally like using a dictionary's get method for this kind of merge:
def add(a, b):
result = {}
for dictionary in (a, b):
for key, value in dictionary.items():
result[key] = result.get(key, 0) + value
return result

How to change one item in dictionary

I need a function to change one item in composite dictionary.
I've tried something like..
def SetItem(keys, value):
item = self.dict
for key in keys:
item = item[key]
item = value
and
SetItem(['key1', 'key2'], 86)
It should be equivalent to self.dict['key1']['key2'] = 86, but this function has no effect.
Almost. You actually want to do something like:
def set_keys(d, keys, value):
item = d
for key in keys[:-1]:
item = item[key]
item[keys[-1]] = value
Or recursively like this:
def set_key(d, keys, value):
if len(keys) == 1:
d[keys[0]] = value
else:
set_key(d[keys[0]], keys[1:], value)
Marcin's right though. You would really want to incorporate something more rigorous, with some error handling for missing keys/missing dicts.
setItem = lambda self,names,value: map((lambda name: setattr(self,name,value)),names)
You don't have a self parameter
Just use the line of working code you have.
If you insist, here's a way:
def setitem(self, keys, value):
reduce(dict.get, # = lambda dictionary, key: dictionary[key]
keys[:-1], self.dictionary)[keys[-1]] = value
Obviously, this will break if the list of keys hits a non-dict value. You'll want to handle that. In fact, an explicit loop would probably be better for that reason, but you get the idea.
An idea involving recursion and EAFP, both of which I always like:
def set_item(d, keys, value):
key = keys.pop(0)
try:
set_item(d[key], keys, value)
# IndexError happens when the pop fails (empty list), KeyError happens when it's not a dict.
# Assume both mean we should finish recursing
except (IndexError, KeyError):
d[key] = value
Example:
>>> d = {'a': {'aa':1, 'ab':2}, 'b':{'ba':1, 'bb':2}}
>>> set_item(d, ['a', 'ab'], 50)
>>> print d
{'a': {'aa': 1, 'ab': 50}, 'b': {'ba': 1, 'bb': 2}}
Edit: As Marcin points out below, this will not work for arbitrarily nested dicts since Python has a recursion limit. It's also not for highly performance-sensitive situations (recursion in Python generally isn't). Nonetheless, outside of these two situations I find this to be somewhat more explicit than something involving reduce or lambda.

Find the index of a dict within a list, by matching the dict's value

I have a list of dicts:
list = [{'id':'1234','name':'Jason'},
{'id':'2345','name':'Tom'},
{'id':'3456','name':'Art'}]
How can I efficiently find the index position [0],[1], or [2] by matching on name = 'Tom'?
If this were a one-dimensional list I could do list.index() but I'm not sure how to proceed by searching the values of the dicts within the list.
lst = [{'id':'1234','name':'Jason'}, {'id':'2345','name':'Tom'}, {'id':'3456','name':'Art'}]
tom_index = next((index for (index, d) in enumerate(lst) if d["name"] == "Tom"), None)
# 1
If you need to fetch repeatedly from name, you should index them by name (using a dictionary), this way get operations would be O(1) time. An idea:
def build_dict(seq, key):
return dict((d[key], dict(d, index=index)) for (index, d) in enumerate(seq))
people_by_name = build_dict(lst, key="name")
tom_info = people_by_name.get("Tom")
# {'index': 1, 'id': '2345', 'name': 'Tom'}
A simple readable version is
def find(lst, key, value):
for i, dic in enumerate(lst):
if dic[key] == value:
return i
return -1
It won't be efficient, as you need to walk the list checking every item in it (O(n)). If you want efficiency, you can use dict of dicts.
On the question, here's one possible way to find it (though, if you want to stick to this data structure, it's actually more efficient to use a generator as Brent Newey has written in the comments; see also tokland's answer):
>>> L = [{'id':'1234','name':'Jason'},
... {'id':'2345','name':'Tom'},
... {'id':'3456','name':'Art'}]
>>> [i for i,_ in enumerate(L) if _['name'] == 'Tom'][0]
1
Seems most logical to use a filter/index combo:
names=[{}, {'name': 'Tom'},{'name': 'Tony'}]
names.index(next(filter(lambda n: n.get('name') == 'Tom', names)))
1
And if you think there could be multiple matches:
[names.index(item) for item in filter(lambda n: n.get('name') == 'Tom', names)]
[1]
Answer offered by #faham is a nice one-liner, but it doesn't return the index to the dictionary containing the value. Instead it returns the dictionary itself. Here is a simple way to get: A list of indexes one or more if there are more than one, or an empty list if there are none:
list = [{'id':'1234','name':'Jason'},
{'id':'2345','name':'Tom'},
{'id':'3456','name':'Art'}]
[i for i, d in enumerate(list) if 'Tom' in d.values()]
Output:
>>> [1]
What I like about this approach is that with a simple edit you can get a list of both the indexes and the dictionaries as tuples. This is the problem I needed to solve and found these answers. In the following, I added a duplicate value in a different dictionary to show how it works:
list = [{'id':'1234','name':'Jason'},
{'id':'2345','name':'Tom'},
{'id':'3456','name':'Art'},
{'id':'4567','name':'Tom'}]
[(i, d) for i, d in enumerate(list) if 'Tom' in d.values()]
Output:
>>> [(1, {'id': '2345', 'name': 'Tom'}), (3, {'id': '4567', 'name': 'Tom'})]
This solution finds all dictionaries containing 'Tom' in any of their values.
Here's a function that finds the dictionary's index position if it exists.
dicts = [{'id':'1234','name':'Jason'},
{'id':'2345','name':'Tom'},
{'id':'3456','name':'Art'}]
def find_index(dicts, key, value):
class Null: pass
for i, d in enumerate(dicts):
if d.get(key, Null) == value:
return i
else:
raise ValueError('no dict with the key and value combination found')
print find_index(dicts, 'name', 'Tom')
# 1
find_index(dicts, 'name', 'Ensnare')
# ValueError: no dict with the key and value combination found
One liner!?
elm = ([i for i in mylist if i['name'] == 'Tom'] or [None])[0]
I needed a more general solution to account for the possibility of multiple dictionaries in the list having the key value, and a straightforward implementation using list comprehension:
dict_indices = [i for i, d in enumerate(dict_list) if d[dict_key] == key_value]
def search(itemID,list):
return[i for i in list if i.itemID==itemID]
The following will return the index for the first matching item:
['Tom' in i['name'] for i in list].index(True)
my answer is better in one a dictionary to use
food_time_dict = {"Lina": 312400, "Tom": 360054, "Den": 245800}
print(list(food_time_dict.keys()).index("Lina"))
I request keys from the dictionary, then I translate the list if it is not added, there will be an error then I use it as a list. but on your code:
lists = [{'id': '1234', 'name': 'Jason'},
{'id': '2345', 'name': 'Tom'},
{'id': '3456', 'name': 'Art'}]
def dict_in_lists_index(lists, search): # function for convenience
j = 0 # [j][i]
for i in lists:
try: # try our varible search if not found in list
return f"[{j}][{list(i.values()).index(search)}]"
# small decor
except ValueError: # error was ValueError
pass # aa... what must was what you want to do
j += 1 # not found? ok j++
return "Not Found"
def dict_cropped_index(lists, search):
for i in lists:
try:
return list(i.values()).index(search)
except ValueError:
pass
return "Not Found"
print(dict_in_lists_index(lists, 'Tom')) # and end
print(dict_cropped_index(lists, 'Tom')) # now for sure end
For a given iterable, more_itertools.locate yields positions of items that satisfy a predicate.
import more_itertools as mit
iterable = [
{"id": "1234", "name": "Jason"},
{"id": "2345", "name": "Tom"},
{"id": "3456", "name": "Art"}
]
list(mit.locate(iterable, pred=lambda d: d["name"] == "Tom"))
# [1]
more_itertools is a third-party library that implements itertools recipes among other useful tools.

Categories

Resources