'Advanced' list comprehension? query
jkrukoff at ltgc.com
Wed Aug 8 19:34:47 CEST 2007
pyscottishguy at hotmail.com wrote:
> I'm playing around with list comprehension, and I'm trying to find the
> most aesthetic way to do the following:
> I have two lists:
> noShowList = ['one', 'two', 'three']
> myList = ['item one', 'item four', 'three item']
> I want to show all the items from 'myList' that do not contain any of
> the strings in 'noShowList'.
> i.e. 'item four'
> I can do it like this:
> def inItem(noShowList, listitem):
> return [x for x in noShowList if x in listitem]
> print [x for x in myList if not inItem(noShowList, x)]
> and I can do it (horribly) with:
> print [x for x in myList if not (lambda y, z:[i for i in y if i in z])
> (noShowList, x)]
> I can also print out the items that DO contain the 'noShowList'
> strings with:
> print [x for x in myList for y in noShowList if y in x]
> but I can't get the 'not' bit to work in the above line.
> Any ideas?
So, conceptually speaking, you're dealing with two loops here, one over the
items to filter, and one over the items to check for substring matches. If
you want to do that with list comprehensions, I'd make it obvious that
there's two of them:
>>> [ listItem for listItem in myList if not [ noShow for noShow in
noShowList if noShow in listItem ] ]
This is a pretty good place for the functional programming tools though,
, which gives a solution that looks like this:
>>> filter( lambda listItem : not [ noShow for noShow in noShowList if
noShow in listItem ], myList )
or using purely functional tools, like this:
>>> filter( lambda listItem : not sum( map( lambda noShow: noShow in
listItem, noShowList ) ), myList )
All these solutions have the problem that they're still less efficient than
the unwrapped for loop, like so:
>>> aFiltered = 
>>> for listItem in myList:
... for noShow in noShowList:
... if noShow in listItem:
... aFiltered.append( listItem )
This is due to the list comprehensions testing all the possiblities, instead
of giving up on the first one found. You can jam that early break into the
functional approach using itertools, but it starts to look really ugly on
one line (requires 2.5 for if expression):
>>> list( itertools.ifilter( lambda listItem : True if len( list(
itertools.takewhile( lambda test : not test, itertools.imap( lambda noShow:
noShow in listItem, noShowList ) ) ) ) == len( noShowList) else False,
myList ) )
Which can be made to look much better by breaking the 'noShow in listItem'
test out into a separate function, and does have the advantage that by using
itertools.ifilter this is a lazy approach. There's got to be a better way to
do the test to see if takewhile bailed early than using len, though.
helot at comcast.net
More information about the Python-list