The key
argument tells min
what to determine the minimum by.
Without the key
argument, min
compares any given whole tuple with the other tuples, and then the first element within the tuple is compared first. The key
function is called for each element in the input sequence and the minimum element is determined solely by the return value of that key. lambda k: k[1]
returns the second element in the tuple.
Compare the following two outcomes:
>>> example = [(5, 1), (4, 2), (3, 3), (2, 4), (1, 5)]
>>> min(example)
(1, 5)
>>> min(example, key=lambda element: element[1])
(5, 1)
In the first example, no key
function is supplied and min()
compares each tuple as-is, in the second example, min()
only looks at what the key()
function returns and thus picks a different element as the minimum.
You can go really overboard with that key function:
>>> min(example, key=lambda element: (element[0] / element[1]) + element[1])
(4, 2)
Using str
is not really needed, and the whole expression is overly verbose; you can simplify it down to:
i = min((x for x in aList if x[2] == 1), key=lambda k: k[1])
or using operator.itemgetter
:
from operater import itemgetter
i = min((x for x in aList if x[2] == 1), key=itemgetter(1))
To compare neighboring pairs, you'd need a itertools
helper function:
from itertools import tee, izip
def pairwise(iterable):
"s -> (s0,s1), (s1,s2), (s2, s3), ..."
a, b = tee(iterable)
next(b, None)
return izip(a, b)
It's easier to then move the 'last element is 1' criteria to a filter, using itertools.ifilter
:
from itertools import ifilter
last_is_one = ifilter(lambda x: x[2] == 1, aList)
paired = pairwise(last_is_one)
Now we can do the real work; for each pair of neighbouring lists, find the pair whose second element sum is the lowest, then from that pair find the lowest by second element:
# find minimum pair by second elements summed
minpair = min(paired, key=lambda pair: pair[0][1] + pair[1][1])
minimum = min(minpair, key=itemgetter(1))
To put that all together, with the responsibility of filtering left to the caller of the function:
from operater import itemgetter
from itertools import tee, izip
def pairwise(iterable):
"s -> (s0,s1), (s1,s2), (s2, s3), ..."
a, b = tee(iterable)
next(b, None)
return izip(a, b)
def neighbouring_minimum(iterable):
paired = pairwise(iterable)
# find minimum pair by second elements summed
minpair = min(paired, key=lambda pair: pair[0][1] + pair[1][1])
return min(minpair, key=itemgetter(1))
For your sample input that gives:
>>> from itertools import ifilter
>>> aList = [[10564, 15, 1], [10564, 13, 1], [10589, 18, 1], [10637, 39, 1], [10662, 38, 1], [10837, 45, 1], [3, 17, 13], [7, 21, 13], [46, 1, 13]]
>>> filtered = ifilter(lambda x: x[2] == 1, aList)
>>> neighbouring_minimum(filtered)
[10564, 13, 1]
You can even move the criteria for the minimum to a separate key argument:
def neighbouring_minimum(iterable, key=None):
if key is None:
# default to the element itself
key = lambda x: x
paired = pairwise(iterable)
# find minimum pair by key summed
minpair = min(paired, key=lambda pair: sum(map(key, pair)))
return min(minpair, key=key)
neighbouring_minimum(ifilter(lambda x: x[2] == 1, aList), key=itemgetter(1))