Reputation: 1147
Suppose I have a function designed to find the largest Y
value in a list of dictionaries.
s1 = [
{'x':10, 'y':8.04},
{'x':8, 'y':6.95},
{'x':13, 'y':7.58},
{'x':9, 'y':8.81},
{'x':11, 'y':8.33},
{'x':14, 'y':9.96},
{'x':6, 'y':7.24},
{'x':4, 'y':4.26},
{'x':12, 'y':10.84},
{'x':7, 'y':4.82},
{'x':5, 'y':5.68},
]
def range_y(list_of_dicts):
y = lambda dict: dict['y']
return y(min(list_of_dicts, key=y)), y(max(list_of_dicts, key=y))
range_y(s1)
This works and gives the intended result.
What I don't understand is the y
before the (min(list_of_dicts, key=y)
. I know I can find the min
and max
with min(list_of_dicts, key=lambda d: d['y'])['y']
where the y
parameter goes at the end (obviously swapping min
for max
).
Can someone explain to me what is happening in y(min(list_of_dicts, key=y))
with the y
and the parenthetical?
Upvotes: 0
Views: 63
Reputation: 77407
I'm declaring Abuse of Lambda. Whenever you see a lambda
assigned to a variable, you need to ask why give a name to an anonymous function? And when that function lacks a clear name, why make this hard? The function could be rewritten as follows:
def get_y(d):
"""Return "y" item from collection `d`"""
return d['y']
def range_y(list_of_dicts):
return get_y(min(list_of_dicts, key=get_y)), get_y(max(list_of_dicts, key=get_y))
In fact, there is a function in the standard lib that does this, so this may be more expected
def range_y(list_of_dicts):
get_y = operator.itemgetter("y")
return get_y(min(list_of_dicts, key=get_y)), get_y(max(list_of_dicts, key=get_y))
But there is a more straight forward way to write this. itemgetter
is useful as a key in the min/max searches, but only confuses things once you've selected the dicts.
def range_y(list_of_dicts):
get_y = operator.itemgetter("y")
return min(list_of_dicts, key=get_y)["y"], max(list_of_dicts, key=get_y)["y"]
But since all you care about is the min/max "y", extract those values and work with them from the beginning.
def range_y(list_of_dicts):
y_vals = [d["y"] for d in list_of_dicts]
return min(y_vals), max(y_vals)
Upvotes: 1
Reputation: 33179
I know I can find the min and max with
min(list_of_dicts, key=lambda d: d['y'])['y']
...
It's exactly the same as that, but the function y
does the indexing. It's a bit shorter and DRYer to write it that way.
Note that named lambdas are generally bad practice, although this case isn't too bad. Best practice is to use operator.itemgetter
:
y = operator.itemgetter('y')
However, you can do it better by using generator expressions to get the min/max y-values directly, instead of their containing dicts. Then the indexing only happens twice, which makes the function y
practically pointless.
return min(d['y'] for d in list_of_dicts), max(d['y'] for d in list_of_dicts)
Upvotes: 1
Reputation: 163
y
is a function, where the function is defined by the lambda
statement. The function accepts a dictionary as an argument, and returns the value at key 'y'
in the dictionary.
min(list_of_dicts, key=y)
returns the dictionary from the list with the smallest value under key 'y'
so putting it together, you get the value at key 'y'
in the dictionary from the list with the smallest value under key 'y'
of all dictionaries in the list
Upvotes: 1