Reputation: 5759
from operator import itemgetter
from itertools import takewhile
xs = [ ('foo',1), ('bar',1), ('baz',2) ]
xs
is sorted on the second item - there are no more 1
s after 'bar'
.
def item_map(xs):
getcount = itemgetter(1)
return list(map(getcount,xs))
print(item_map(xs))
>>> [1, 1, 2]
returns a list of the second element of every tuple.
def item_take(xs):
return list(takewhile(lambda x: x[1] == 1, xs))
print(item_take(xs))
[('foo', 1), ('bar', 1)]
returns the tuples that have a second element that == 1.
def could_this_work(xs):
match = itemgetter(1) == 1
return list(takewhile(match, xs))
print(could_this_work(xs))
TypeError: 'bool' object is not callable
does not return the tuples that have a second element that == 1
Is there a way to use itemgetter
in place of the lambda? Or can itemgetter
not be used in this way?
EDIT. takewhile
is being used for a reason. I understand what it does. This function is going to be used on a sorted list. I appreciate that the tuples are backwards for this, but the code that I have used it correct for what I want and expect.
Upvotes: 2
Views: 252
Reputation: 602115
Your lambda function is actually the composition of two functions: operator.itemgetter(1)
and operator.eq
. Doing this in a purely functional style would require a compose()
function, like this one:
def compose(f, g):
def composed(x):
return f(g(x))
return composed
Using this function, you could do
from operator import itemgetter, eq
from functools import partial
def take_items(a):
return takewhile(compose(partial(eq, 1), itemgetter(1)), a)
I don't think this is a godd idea, though. I would probably go with the straight-forward
def take_items(a):
for x in a:
if x[1] != 1:
break
yield x
I think this requires less thinking on part of the reader of the code.
Upvotes: 2
Reputation: 61607
Please don't do this in real code. Just use the lambda, honestly.
from operator import itemgetter, eq
from functools import partial
from itertools import takewhile
def compose_unary(func1, func2):
return lambda x: func1(func2(x))
def item_take(xs):
return list(takewhile(compose_unary(partial(eq, 1), itemgetter(1)), xs))
Note that takewhile
doesn't actually do quite what you seem to think; it will stop at the first element that doesn't match the predicate, and ignore anything beyond that point that does.
Upvotes: 0
Reputation: 375754
itemgetter
doesn't do comparisons, it simply gives you a function that retrieves items. You'll need to make your own function if you want comparisons.
Also, note that you can use list comprehensions:
def could_this_work(xs):
return [x for x in xs if x[1] == 1]
or even generator expressions, which can work lazily on even infinite streams:
def could_this_work(xs):
return (x for x in xs if x[1] == 1)
(These do what your English said: get the items with 1 in the second element. If you want to stop when you find a non-1 element, use Sven's answer.)
Upvotes: 1
Reputation: 7078
Try:
getcount = itemgetter(1)
match = lambda x: getcount(x) == 1
What you did commpared the itemgetter(1)
to 1. This comaprison returns False. Then you call that. False(x)
won't work, so you have this error.
itemgetter(n)
is basically a function similar to:
def itemgetter(n):
return lambda x: x[n]
You notice that returns another function, comparing it to an int
makes no sense.
Upvotes: 2