Python: selecting an item in a list where dict key is some value using lambda

Is it possible to get using lambda? I know that we can make a sorted function with a lambda and its VERY useful.

Is there a short form way to retrieve an object in a list in which the object in the 'id' key is equal to message 20 ?

Of course, we can use the cycle and the cycle throughout the subject.

 x = [ {'Car': 'Honda', 'id': 12}, {'Car': 'Mazda', 'id': 45}, {'Car': 'Toyota', 'id': 20} ] desired_val = None for item in list: if item['id'] == 20: desired_val = item break 

Is it possible to achieve the same functionality using lambda ? I am not very good at lambda .

+6
source share
4 answers

I would suggest that your own method be the best way to find the first item in a list that matches the criteria.

This is straightforward and will exit the loop after the desired target is found.

It is also the fastest. Here, compared to numerous ways to return a FIRST dict to a list using 'id'==20 :

 from __future__ import print_function def f1(LoD, idd=20): # loop until first one is found then break and return the dict found desired_dict = None for di in LoD: if di['id'] == idd: desired_dict = di break return desired_dict def f2(LoD, idd=20): # The genexp goes through the entire list, then next() returns either the first or None return next((di for di in LoD if di['id'] == idd), None) def f3(LoD, idd=20): # NOTE: the 'filter' here is ifilter if Python2 return next(filter(lambda di: di['id']==idd, LoD), None) def f4(LoD, idd=20): desired_dict=None i=0 while True: try: if LoD[i]['id']==idd: desired_dict=LoD[i] break else: i+=1 except IndexError: break return desired_dict def f5(LoD, idd=20): try: return [d for d in LoD if d['id']==idd][0] except IndexError: return None if __name__ =='__main__': import timeit import sys if sys.version_info.major==2: from itertools import ifilter as filter x = [ {'Car': 'Honda', 'id': 12}, {'Car': 'Mazda', 'id': 45}, {'Car': 'Toyota', 'id': 20} ] * 10 # the '* 10' makes a list of 30 dics... result=[] for f in (f1, f2, f3, f4, f5): fn=f.__name__ fs="f(x, idd=20)" ft=timeit.timeit(fs, setup="from __main__ import x, f", number=1000000) r=eval(fs) result.append((ft, fn, r, )) result.sort(key=lambda t: t[0]) for i, t in enumerate(result): ft, fn, r = t if i==0: fr='{}: {:.4f} secs is fastest\n\tf(x)={}\n========'.format(fn, ft, r) else: t1=result[0][0] dp=(ft-t1)/t1 fr='{}: {:.4f} secs - {} is {:.2%} faster\n\tf(x)={}'.format(fn, ft, result[0][1], dp, r) print(fr) 

If the value 'id'==20 found, prints:

 f1: 0.4324 secs is fastest f(x)={'Car': 'Toyota', 'id': 20} ======== f4: 0.6963 secs - f1 is 61.03% faster f(x)={'Car': 'Toyota', 'id': 20} f3: 0.9077 secs - f1 is 109.92% faster f(x)={'Car': 'Toyota', 'id': 20} f2: 0.9840 secs - f1 is 127.56% faster f(x)={'Car': 'Toyota', 'id': 20} f5: 2.6065 secs - f1 is 502.77% faster f(x)={'Car': 'Toyota', 'id': 20} 

And, if not found, prints:

 f1: 1.6084 secs is fastest f(x)=None ======== f2: 2.0128 secs - f1 is 25.14% faster f(x)=None f5: 2.5494 secs - f1 is 58.50% faster f(x)=None f3: 4.4643 secs - f1 is 177.56% faster f(x)=None f4: 5.7889 secs - f1 is 259.91% faster f(x)=None 

Of course, as written, these functions return only the first dict in this list with 'id' == 20. If you want ALL of them, you can use a list comprehension or a filter using lambda.

You can see that once you have written a function modified to return a list, it is still competitive:

 def f1(LoD, idd): desired_lst = [] for item in LoD: if item['id'] == idd: desired_lst.append(item) return desired_lst def f2(LoD, idd): return [d for d in LoD if d['id']==idd] def f3(LoD, idd): return list(filter(lambda x: x['id']==idd, LoD) ) 

Using the same code for time, these functions print:

 f2: 2.3849 secs is fastest f(x)=[{'Car': 'Toyota', 'id': 20}, {'Car': 'Toyota', 'id': 20}, {'Car': 'Toyota', 'id': 20}, {'Car': 'Toyota', 'id': 20}, {'Car': 'Toyota', 'id': 20}, {'Car': 'Toyota', 'id': 20}, {'Car': 'Toyota', 'id': 20}, {'Car': 'Toyota', 'id': 20}, {'Car': 'Toyota', 'id': 20}, {'Car': 'Toyota', 'id': 20}] ======== f1: 3.0051 secs - f2 is 26.00% faster f(x)=[{'Car': 'Toyota', 'id': 20}, {'Car': 'Toyota', 'id': 20}, {'Car': 'Toyota', 'id': 20}, {'Car': 'Toyota', 'id': 20}, {'Car': 'Toyota', 'id': 20}, {'Car': 'Toyota', 'id': 20}, {'Car': 'Toyota', 'id': 20}, {'Car': 'Toyota', 'id': 20}, {'Car': 'Toyota', 'id': 20}, {'Car': 'Toyota', 'id': 20}] f3: 5.2386 secs - f2 is 119.66% faster f(x)=[{'Car': 'Toyota', 'id': 20}, {'Car': 'Toyota', 'id': 20}, {'Car': 'Toyota', 'id': 20}, {'Car': 'Toyota', 'id': 20}, {'Car': 'Toyota', 'id': 20}, {'Car': 'Toyota', 'id': 20}, {'Car': 'Toyota', 'id': 20}, {'Car': 'Toyota', 'id': 20}, {'Car': 'Toyota', 'id': 20}, {'Car': 'Toyota', 'id': 20}] 

In this case, understanding the list is better.

+4
source

Using lambda, as you requested, with a generator expression that is generally considered more readable than a filter, and note that this works equally well in Python 2 or 3.

 lambda x: next(i for i in x if i['id'] == 20) 

Using:

 >>> foo = lambda x: next(i for i in x if i['id'] == 20) >>> foo(x) {'Car': 'Toyota', 'id': 20} 

And this use of lambda is probably not very useful. We can easily define a function:

 def foo(x): return next(i for i in x if i['id'] == 20) 

But we can give him docstrings, and he knows his name and has other interesting attributes that anonymous functions (which we then call) do not have.

Also, I really think what you get is part of the expression filter.

IN

 filter(lambda x: x[id]==20, x) 

we replaced this functionality with the conditional part of the generator expression. The functional part of generator expressions (understanding lists in square brackets) similarly replaces map .

+5
source

Using lambda is not required here. Lambda is not something magical, it's just a shorthand for writing a simple function. This is less powerful than the usual way of writing a function, not more. (Not to say, sometimes it’s not very convenient, just that he does not have super powers.)

In either case, you can use a generator expression with a default argument. Please note that here I return the object itself, not 20, because it makes more sense to me.

 >>> somelist = [{"id": 10, "x": 1}, {"id": 20, "y": 2}, {"id": 30, "z": 3}] >>> desired_val = next((item for item in somelist if item['id'] == 20), None) >>> print(desired_val) {'y': 2, 'id': 20} >>> desired_val = next((item for item in somelist if item['id'] == 21), None) >>> print(desired_val) None 
+5
source

In Py3k, the filter returns an iterator, so you can use next to get its first value:

 val = next(filter(lambda x: x['id'] == 20, list)) 

For Python 2, use itertools.ifilter because the built-in filter creates a list with the results:

 from itertools import ifilter val = next(ifilter(lambda x: x['id'] == 20, list)) 

Consider passing the default value to next , which will be returned if the iterator is empty:

 In [3]: next(filter(bool, [False]), 'default value here') Out[3]: 'default value here' 
+4
source

Source: https://habr.com/ru/post/971502/


All Articles