Search code examples
pythonpython-3.xpython-itertools

testing for equality using itemgetter


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 1s 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.


Solution

  • 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.