'Advanced' list comprehension? query

John Krukoff jkrukoff at ltgc.com
Wed Aug 8 13:45:49 EDT 2007


John Krukoff wrote:
> 
> pyscottishguy at hotmail.com wrote:
> > Hi,
> >
> > 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?
> > Thanks!
> >
> > --
> > http://mail.python.org/mailman/listinfo/python-list
> 
> 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 ] ]
> ['item four']
> 
> This is a pretty good place for the functional programming tools though,
> specifically "filter",
> http://docs.python.org/tut/node7.html#SECTION007130000000000000000
> , which gives a solution that looks like this:
> 
> >>> filter( lambda listItem : not [ noShow for noShow in noShowList if
> noShow in listItem ], myList )
> ['item four']
> 
> or using purely functional tools, like this:
> 
> >>> filter( lambda listItem : not sum( map( lambda noShow: noShow in
> listItem, noShowList ) ), myList )
> ['item four']
> 
> 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:
> ...             break
> ...     else:
> ...         aFiltered.append( listItem )
> ...
> >>> aFiltered
> ['item four']
> 
> 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 ) )
> ['item four']
> 
> 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.
> 
> ---------
> John Krukoff
> helot at comcast.net

Ah, yeah, any was the function I needed to simplify things and do the early
break. I really need to upgrade to 2.5.

>>> list( itertools.ifilter( lambda listItem : not any( itertools.imap(
lambda noShow: noShow in listItem, noShowList ) ), myList ) )
['item four']

Thanks Jason!

---------
John Krukoff
helot at comcast.net





More information about the Python-list mailing list