Skip to content Skip to sidebar Skip to footer

Is There A Way To Capture Misses On A List Comprehension?

Based on a simple list comprehension : yay = [ i for i in a if a[i] ] nay = [ i for i in a if not a[i] ] I am wondering if there is a way to assign both the yay and nay values at

Solution 1:

The usual solution here is not to get all hung up on the idea of using a list comprehension. Just use a for loop:

yay, nay = [], []
for i in a:
    if somecondition(i):
        yay.append(i)
    else:
        nay.append(i)

If you find yourself doing this a lot then simply move the code out into a function:

defyesno(seq, cond):
    yay, nay = [], []
    for i in seq:
        if cond(i):
            yay.append(i)
        else:
            nay.append(i)
    return yay, nay

yay, nay = yesno(a, lambda x: a[x])

The comments suggest this is slower than list comprehension. Passing the condition as a lambda is inevitably going to have a big hit and I don't think you can do much about that, but some of the performance hit probably comes from looking up the append method and that can be improved:

def yesno(seq, cond):
    yay, nay = [], []
    yes, no = yay.append, nay.append
    for i inseq:
        if cond(i):
            yes(i)
        else:
            no(i)
    return yay, nay

I don't know if that makes much of a difference, but it might be interesting to time it.

In the comments @martineau suggests using a generator and consuming it with any(). I'll include that here, but I would replace any with the itertools recipe to consume an iterator:

defconsume(iterator, n):
    "Advance the iterator n-steps ahead. If n is none, consume entirely."# Use functions that consume iterators at C speed.if n isNone:
        # feed the entire iterator into a zero-length deque
        collections.deque(iterator, maxlen=0)
    else:
        # advance to the empty slice starting at position nnext(islice(iterator, n, n), None)

and then you can write:

yay, nay = [], []
consume((yay if a[i] else nay).append(i) for i in a)

Solution 2:

I would still say, your way of doing is more readable and should be the suggested approach, but in any case, if you are looking for alternative, you can look forward with a solution from itertools

>>>from itertools import compress, imap>>>from operator import not_>>>yay, nay = compress(a,a.values()), compress(a, imap(not_,a.values()))

Solution 3:

You might be able to use a dict comprehension, but I'm reasonably sure you can't* use list comprehension to do what you ask. Assuming the data is or can be sorted** I would probably use itertools.groupby.

results = itertools.groupby(sorted_a, bool)

*Qualification: OK, Lattyware's answer shows that you can, but it also generates a tuple with an None value for each member of the iterable. IMO that's a lot of waste. While I confess I didn't even consider that, I'm not ashamed that I didn't.

**Sorted: It needs to be sorted by the same key as it's grouped by.

Solution 4:

This could be done with something like this:

yay, nay = zip(*[(k, None) if v else (None, k) for k, v in a.items()])
yay, nay = filter(None, yay), filter(None, nay)

As to if it would be faster... maybe for huge lists. Otherwise, it's probably not going to matter.

Naturally, if None is a value in your lists, you will need to swap it out for another sentinel and do an identity check with filter().

Solution 5:

It's not pretty, but you could do something among this lines:

nay = []
yay = [foo for foo in foos if condition or nay.append(foo)]

This takes advantage of the short-circuit on the or operator.

Post a Comment for "Is There A Way To Capture Misses On A List Comprehension?"