Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.python > #47603 > unrolled thread

Split a list into two parts based on a filter?

Started byroy@panix.com (Roy Smith)
First post2013-06-10 16:34 -0400
Last post2013-06-11 22:22 +0200
Articles 20 on this page of 38 — 17 participants

Back to article view | Back to comp.lang.python


Contents

  Split a list into two parts based on a filter? roy@panix.com (Roy Smith) - 2013-06-10 16:34 -0400
    Re: Split a list into two parts based on a filter? Chris Angelico <rosuav@gmail.com> - 2013-06-11 08:50 +1000
    Re: Split a list into two parts based on a filter? Roel Schroeven <roel@roelschroeven.net> - 2013-06-11 00:50 +0200
      Re: Split a list into two parts based on a filter? Roy Smith <roy@panix.com> - 2013-06-11 00:11 -0400
        Re: Split a list into two parts based on a filter? Serhiy Storchaka <storchaka@gmail.com> - 2013-06-11 18:27 +0300
          Re: Split a list into two parts based on a filter? Roy Smith <roy@panix.com> - 2013-06-11 20:33 -0400
            Re: Split a list into two parts based on a filter? Phil Connell <pconnell@gmail.com> - 2013-06-12 07:32 +0100
              Re: Split a list into two parts based on a filter? Roy Smith <roy@panix.com> - 2013-06-12 07:39 -0400
                Re: Split a list into two parts based on a filter? Fábio Santos <fabiosantosart@gmail.com> - 2013-06-12 12:51 +0100
                Re: Split a list into two parts based on a filter? Jussi Piitulainen <jpiitula@ling.helsinki.fi> - 2013-06-12 15:06 +0300
                Re: Split a list into two parts based on a filter? Terry Reedy <tjreedy@udel.edu> - 2013-06-12 14:07 -0400
            Re: Split a list into two parts based on a filter? Serhiy Storchaka <storchaka@gmail.com> - 2013-06-12 19:28 +0300
            Re: Split a list into two parts based on a filter? Fábio Santos <fabiosantosart@gmail.com> - 2013-06-12 17:57 +0100
            Re: Split a list into two parts based on a filter? Terry Reedy <tjreedy@udel.edu> - 2013-06-12 14:47 -0400
            Re: Split a list into two parts based on a filter? Oscar Benjamin <oscar.j.benjamin@gmail.com> - 2013-06-13 10:43 +0100
    Re: Split a list into two parts based on a filter? Chris Rebert <clp2@rebertia.com> - 2013-06-10 16:03 -0700
    Re: Split a list into two parts based on a filter? Tim Chase <python.list@tim.thechases.com> - 2013-06-10 18:10 -0500
    Re: Split a list into two parts based on a filter? Fábio Santos <fabiosantosart@gmail.com> - 2013-06-11 00:08 +0100
      Re: Split a list into two parts based on a filter? alex23 <wuwei23@gmail.com> - 2013-06-11 17:44 -0700
    Re: Split a list into two parts based on a filter? Chris Angelico <rosuav@gmail.com> - 2013-06-11 09:12 +1000
    Re: Split a list into two parts based on a filter? Peter Otten <__peter__@web.de> - 2013-06-11 02:11 +0200
    Re: Split a list into two parts based on a filter? Peter Otten <__peter__@web.de> - 2013-06-11 08:43 +0200
    Re: Split a list into two parts based on a filter? Jonas Geiregat <jonas@geiregat.org> - 2013-06-11 08:47 +0200
    Re: Split a list into two parts based on a filter? Fábio Santos <fabiosantosart@gmail.com> - 2013-06-11 14:48 +0100
      Re: Split a list into two parts based on a filter? rusi <rustompmody@gmail.com> - 2013-06-11 09:37 -0700
        Re: Split a list into two parts based on a filter? Fábio Santos <fabiosantosart@gmail.com> - 2013-06-11 18:05 +0100
          Re: Split a list into two parts based on a filter? rusi <rustompmody@gmail.com> - 2013-06-11 10:23 -0700
            Re: Split a list into two parts based on a filter? Chris Angelico <rosuav@gmail.com> - 2013-06-12 03:37 +1000
              Re: Split a list into two parts based on a filter? rusi <rustompmody@gmail.com> - 2013-06-11 11:13 -0700
            Re: Split a list into two parts based on a filter? Fábio Santos <fabiosantosart@gmail.com> - 2013-06-11 19:05 +0100
    Re: Split a list into two parts based on a filter? Joshua Landau <joshua.landau.ws@gmail.com> - 2013-06-11 15:22 +0100
    Re: Split a list into two parts based on a filter? Serhiy Storchaka <storchaka@gmail.com> - 2013-06-11 18:28 +0300
    Re: Split a list into two parts based on a filter? Chris Angelico <rosuav@gmail.com> - 2013-06-12 03:28 +1000
      Re: Split a list into two parts based on a filter? Roy Smith <roy@panix.com> - 2013-06-11 20:12 -0400
    Re: Split a list into two parts based on a filter? Peter Otten <__peter__@web.de> - 2013-06-11 20:13 +0200
    Re: Split a list into two parts based on a filter? Peter Otten <__peter__@web.de> - 2013-06-11 20:18 +0200
    Re: Split a list into two parts based on a filter? Chris Angelico <rosuav@gmail.com> - 2013-06-12 04:27 +1000
    Re: Split a list into two parts based on a filter? Roel Schroeven <roel@roelschroeven.net> - 2013-06-11 22:22 +0200

Page 1 of 2  [1] 2  Next page →


#47603 — Split a list into two parts based on a filter?

Fromroy@panix.com (Roy Smith)
Date2013-06-10 16:34 -0400
SubjectSplit a list into two parts based on a filter?
Message-ID<kp5d9e$2hf$1@panix2.panix.com>
I have a list, songs, which I want to divide into two groups.
Essentially, I want:

new_songs = [s for s in songs if s.is_new()]
old_songs = [s for s in songs if not s.is_new()]

but I don't want to make two passes over the list.  I could do:

new_songs = []
old_songs = []
for s in songs:
    if s.is_new():
        new_songs.append(s)
    else:
        old_songs.append(s)

Which works, but is klunky compared to the two-liner above.  This
seems like a common enough thing that I was expecting to find
something in itertools which did this.  I'm thinking something along
the lines of:

matches, non_matches = isplit(lambda s: s.is_new, songs)

Does such a thing exist?

[toc] | [next] | [standalone]


#47611

FromChris Angelico <rosuav@gmail.com>
Date2013-06-11 08:50 +1000
Message-ID<mailman.2991.1370904631.3114.python-list@python.org>
In reply to#47603
On Tue, Jun 11, 2013 at 6:34 AM, Roy Smith <roy@panix.com> wrote:
> new_songs = [s for s in songs if s.is_new()]
> old_songs = [s for s in songs if not s.is_new()]

Hmm. Would this serve?

old_songs = songs[:]
new_songs = [songs.remove(s) or s for s in songs if s.is_new()]

Python doesn't, AFAIK, have a "destructive remove and return"
operation, and del is a statement rather than an expression/operator,
but maybe this basic idea could be refined into something more useful.
It guarantees to call is_new only once per song.

The iterator version strikes my fancy. Maybe this isn't of use to you,
but I'm going to try my hand at making one anyway.

>>> def iterpartition(pred,it):
	"""Partition an iterable based on a predicate.

	Returns two iterables, for those with pred False and those True."""
	falses,trues=[],[]
	it=iter(it)
	def get_false():
		while True:
			if falses: yield falses.pop(0)
			else:
				while True:
					val=next(it)
					if pred(val): trues.append(val)
					else: break
				yield val
	def get_true():
		while True:
			if trues: yield trues.pop(0)
			else:
				while True:
					val=next(it)
					if not pred(val): falses.append(val)
					else: break
				yield val
	return get_false(),get_true()
>>> f,t=iterpartition(lambda x: x%3,range(100000000))
>>> next(t)
1
>>> next(t)
2
>>> next(t)
4
>>> next(t)
5
>>> next(f)
0
>>> next(f)
3
>>> next(f)
6
>>> next(f)
9
>>> next(f)
12
>>> next(t)
7
>>> next(t)
8

Ha. :) Useless but fun.

ChrisA

[toc] | [prev] | [next] | [standalone]


#47612

FromRoel Schroeven <roel@roelschroeven.net>
Date2013-06-11 00:50 +0200
Message-ID<mailman.2992.1370904643.3114.python-list@python.org>
In reply to#47603
Roy Smith schreef:
> I have a list, songs, which I want to divide into two groups.
> Essentially, I want:
> 
> new_songs = [s for s in songs if s.is_new()]
> old_songs = [s for s in songs if not s.is_new()]
> 
> but I don't want to make two passes over the list.  I could do:
> 
> new_songs = []
> old_songs = []
> for s in songs:
>     if s.is_new():
>         new_songs.append(s)
>     else:
>         old_songs.append(s)
> 
> Which works, but is klunky compared to the two-liner above.  This
> seems like a common enough thing that I was expecting to find
> something in itertools which did this.  I'm thinking something along
> the lines of:
> 
> matches, non_matches = isplit(lambda s: s.is_new, songs)
> 
> Does such a thing exist?

You could do something like:

new_songs, old_songs = [], []
[(new_songs if s.is_new() else old_songs).append(s) for s in songs]

But I'm not sure that that's any better than the long version.

-- 
"People almost invariably arrive at their beliefs not on the basis of
proof but on the basis of what they find attractive."
         -- Pascal Blaise

roel@roelschroeven.net

[toc] | [prev] | [next] | [standalone]


#47637

FromRoy Smith <roy@panix.com>
Date2013-06-11 00:11 -0400
Message-ID<roy-12F8FA.00111311062013@news.panix.com>
In reply to#47612
In article <mailman.2992.1370904643.3114.python-list@python.org>,
 Roel Schroeven <roel@roelschroeven.net> wrote:

> new_songs, old_songs = [], []
> [(new_songs if s.is_new() else old_songs).append(s) for s in songs]

Thanks kind of neat, thanks.

I'm trying to figure out what list gets created and discarded.  I think 
it's [None] * len(songs).

[toc] | [prev] | [next] | [standalone]


#47665

FromSerhiy Storchaka <storchaka@gmail.com>
Date2013-06-11 18:27 +0300
Message-ID<mailman.3023.1370964449.3114.python-list@python.org>
In reply to#47637
11.06.13 07:11, Roy Smith написав(ла):
> In article <mailman.2992.1370904643.3114.python-list@python.org>,
>   Roel Schroeven <roel@roelschroeven.net> wrote:
>
>> new_songs, old_songs = [], []
>> [(new_songs if s.is_new() else old_songs).append(s) for s in songs]
>
> Thanks kind of neat, thanks.
>
> I'm trying to figure out what list gets created and discarded.  I think
> it's [None] * len(songs).

It is the same as your klunky code, but consumes more memory.

[toc] | [prev] | [next] | [standalone]


#47716

FromRoy Smith <roy@panix.com>
Date2013-06-11 20:33 -0400
Message-ID<roy-10ED1B.20334411062013@news.panix.com>
In reply to#47665
In article <mailman.3023.1370964449.3114.python-list@python.org>,
 Serhiy Storchaka <storchaka@gmail.com> wrote:

> 11.06.13 07:11, Roy Smith написав(ла):
> > In article <mailman.2992.1370904643.3114.python-list@python.org>,
> >   Roel Schroeven <roel@roelschroeven.net> wrote:
> >
> >> new_songs, old_songs = [], []
> >> [(new_songs if s.is_new() else old_songs).append(s) for s in songs]
> >
> > Thanks kind of neat, thanks.
> >
> > I'm trying to figure out what list gets created and discarded.  I think
> > it's [None] * len(songs).
> 
> It is the same as your klunky code, but consumes more memory.

Well, continuing down this somewhat bizarre path:

new_songs, old_songs = [], []
itertools.takewhile(
    lambda x: True,
    (new_songs if s.is_new() else old_songs).append(s) for s in songs)
    )

I'm not sure I got the syntax exactly right, but the idea is anything 
that will iterate over a generator expression.  That at least gets rid 
of the memory requirement to hold the throw-away list :-)

[toc] | [prev] | [next] | [standalone]


#47743

FromPhil Connell <pconnell@gmail.com>
Date2013-06-12 07:32 +0100
Message-ID<mailman.3050.1371018754.3114.python-list@python.org>
In reply to#47716

[Multipart message — attachments visible in raw view] — view raw

On 12 Jun 2013 01:36, "Roy Smith" <roy@panix.com> wrote:
>
> In article <mailman.3023.1370964449.3114.python-list@python.org>,
>  Serhiy Storchaka <storchaka@gmail.com> wrote:
>
> > 11.06.13 07:11, Roy Smith написав(ла):
> > > In article <mailman.2992.1370904643.3114.python-list@python.org>,
> > >   Roel Schroeven <roel@roelschroeven.net> wrote:
> > >
> > >> new_songs, old_songs = [], []
> > >> [(new_songs if s.is_new() else old_songs).append(s) for s in songs]
> > >
> > > Thanks kind of neat, thanks.
> > >
> > > I'm trying to figure out what list gets created and discarded.  I
think
> > > it's [None] * len(songs).
> >
> > It is the same as your klunky code, but consumes more memory.
>
> Well, continuing down this somewhat bizarre path:
>
> new_songs, old_songs = [], []
> itertools.takewhile(
>     lambda x: True,
>     (new_songs if s.is_new() else old_songs).append(s) for s in songs)
>     )
>
> I'm not sure I got the syntax exactly right, but the idea is anything
> that will iterate over a generator expression.  That at least gets rid
> of the memory requirement to hold the throw-away list :-)

You could equivalently pass the generator to deque() with maxlen=0 - this
consumes the iterator with constant memory usage.

We are of course firmly in the twilight zone at this point (although this
can be a useful technique in general).

Cheers,
Phil

>
> --
> http://mail.python.org/mailman/listinfo/python-list
>

[toc] | [prev] | [next] | [standalone]


#47785

FromRoy Smith <roy@panix.com>
Date2013-06-12 07:39 -0400
Message-ID<roy-84E0BB.07395112062013@news.panix.com>
In reply to#47743
In article <mailman.3050.1371018754.3114.python-list@python.org>,
 Phil Connell <pconnell@gmail.com> wrote:

> > Well, continuing down this somewhat bizarre path:
> >
> > new_songs, old_songs = [], []
> > itertools.takewhile(
> >     lambda x: True,
> >     (new_songs if s.is_new() else old_songs).append(s) for s in songs)
> >     )
> >
> > I'm not sure I got the syntax exactly right, but the idea is anything
> > that will iterate over a generator expression.  That at least gets rid
> > of the memory requirement to hold the throw-away list :-)
> 
> You could equivalently pass the generator to deque() with maxlen=0 - this
> consumes the iterator with constant memory usage.
> 
> We are of course firmly in the twilight zone at this point (although this
> can be a useful technique in general).

We've been in the twilight zone for a while.  That's when the fun 
starts.  But, somewhat more seriously, I wonder what, exactly, it is 
that freaks people out about:

>>>> [(new_songs if s.is_new() else old_songs).append(s) for s in songs]

Clearly, it's not the fact that it build and immediately discards a 
list, because that concern is addressed with the generator hack, and I 
think everybody (myself included) agrees that's just horrible.

Or, is it the use of the conditional to create the target for append()?  
Would people be as horrified if I wrote:

for s in songs:
    (new_songs if s.is_new() else old_songs).append(s)

or even:

for s in songs:
    the_right_list = new_songs if s.is_new() else old_songs
    the_right_list.append(s)

[toc] | [prev] | [next] | [standalone]


#47788

FromFábio Santos <fabiosantosart@gmail.com>
Date2013-06-12 12:51 +0100
Message-ID<mailman.3064.1371037923.3114.python-list@python.org>
In reply to#47785

[Multipart message — attachments visible in raw view] — view raw

On 12 Jun 2013 12:43, "Roy Smith" <roy@panix.com> wrote:
>
> In article <mailman.3050.1371018754.3114.python-list@python.org>,
>  Phil Connell <pconnell@gmail.com> wrote:
>
> > > Well, continuing down this somewhat bizarre path:
> > >
> > > new_songs, old_songs = [], []
> > > itertools.takewhile(
> > >     lambda x: True,
> > >     (new_songs if s.is_new() else old_songs).append(s) for s in songs)
> > >     )
> > >
> > > I'm not sure I got the syntax exactly right, but the idea is anything
> > > that will iterate over a generator expression.  That at least gets rid
> > > of the memory requirement to hold the throw-away list :-)
> >
> > You could equivalently pass the generator to deque() with maxlen=0 -
this
> > consumes the iterator with constant memory usage.
> >
> > We are of course firmly in the twilight zone at this point (although
this
> > can be a useful technique in general).
>
> We've been in the twilight zone for a while.  That's when the fun
> starts.  But, somewhat more seriously, I wonder what, exactly, it is
> that freaks people out about:
>
> >>>> [(new_songs if s.is_new() else old_songs).append(s) for s in songs]
>
> Clearly, it's not the fact that it build and immediately discards a
> list, because that concern is addressed with the generator hack, and I
> think everybody (myself included) agrees that's just horrible.

I think someone has already said that the problem is that you're creating
the list comprehension just for the side effects.

> Or, is it the use of the conditional to create the target for append()?

I particularly liked that part.

[toc] | [prev] | [next] | [standalone]


#47789

FromJussi Piitulainen <jpiitula@ling.helsinki.fi>
Date2013-06-12 15:06 +0300
Message-ID<qotobbb354j.fsf@ruuvi.it.helsinki.fi>
In reply to#47785
Roy Smith writes:

> We've been in the twilight zone for a while.  That's when the fun
> starts.  But, somewhat more seriously, I wonder what, exactly, it is
> that freaks people out about:
> 
> >>>> [(new_songs if s.is_new() else old_songs).append(s) for s in songs]
> 
> Clearly, it's not the fact that it build and immediately discards a
> list, because that concern is addressed with the generator hack, and
> I think everybody (myself included) agrees that's just horrible.

I expect e(s) in [e(s) for s in songs] to be an expression that is
evaluated for its value and not for an effect. The comprehension
should stand for a list of those values. The one above violates this
expectation.

> Or, is it the use of the conditional to create the target for append()?  
> Would people be as horrified if I wrote:
> 
> for s in songs:
>     (new_songs if s.is_new() else old_songs).append(s)
> 
> or even:
> 
> for s in songs:
>     the_right_list = new_songs if s.is_new() else old_songs
>     the_right_list.append(s)

These are fine.

[toc] | [prev] | [next] | [standalone]


#47828

FromTerry Reedy <tjreedy@udel.edu>
Date2013-06-12 14:07 -0400
Message-ID<mailman.3117.1371060481.3114.python-list@python.org>
In reply to#47785
On 6/12/2013 7:39 AM, Roy Smith wrote:

> starts.  But, somewhat more seriously, I wonder what, exactly, it is
> that freaks people out about:
>
>>>>> [(new_songs if s.is_new() else old_songs).append(s) for s in songs]
>
> Clearly, it's not the fact that it build and immediately discards a
> list, because that concern is addressed with the generator hack, and I
> think everybody (myself included) agrees that's just horrible.

It is an example of comprehension abuse. Comprehensions express and 
condense a stylized pattern of creating collections from another 
collection or collections, possibly filtered. They were not mean to 
replace for statements and turn Python into an fp languages. Indeed, 
they do replace and expand upon the fp map function. Python for loops 
are not evil.

> Or, is it the use of the conditional to create the target for append()?
> Would people be as horrified if I wrote:
>
> for s in songs:
>      (new_songs if s.is_new() else old_songs).append(s)


No. That succinctly expresses and implements the idea 'append each song 
to one of two lists.

> or even:
>
> for s in songs:
>      the_right_list = new_songs if s.is_new() else old_songs
>      the_right_list.append(s)
>


-- 
Terry Jan Reedy

[toc] | [prev] | [next] | [standalone]


#47816

FromSerhiy Storchaka <storchaka@gmail.com>
Date2013-06-12 19:28 +0300
Message-ID<mailman.3112.1371054538.3114.python-list@python.org>
In reply to#47716
12.06.13 09:32, Phil Connell написав(ла):
> On 12 Jun 2013 01:36, "Roy Smith" <roy@panix.com <mailto:roy@panix.com>>
> wrote:
>  > Well, continuing down this somewhat bizarre path:
>  >
>  > new_songs, old_songs = [], []
>  > itertools.takewhile(
>  >     lambda x: True,
>  >     (new_songs if s.is_new() else old_songs).append(s) for s in songs)
>  >     )
>  >
>  > I'm not sure I got the syntax exactly right, but the idea is anything
>  > that will iterate over a generator expression.  That at least gets rid
>  > of the memory requirement to hold the throw-away list :-)
>
> You could equivalently pass the generator to deque() with maxlen=0 -
> this consumes the iterator with constant memory usage.

any((new_songs if s.is_new() else old_songs).append(s) for s in songs)

[toc] | [prev] | [next] | [standalone]


#47818

FromFábio Santos <fabiosantosart@gmail.com>
Date2013-06-12 17:57 +0100
Message-ID<mailman.3114.1371056243.3114.python-list@python.org>
In reply to#47716
Why is there no builtin to consume a generator? I find that odd.

On Wed, Jun 12, 2013 at 5:28 PM, Serhiy Storchaka <storchaka@gmail.com> wrote:
> 12.06.13 09:32, Phil Connell написав(ла):
>>
>> On 12 Jun 2013 01:36, "Roy Smith" <roy@panix.com <mailto:roy@panix.com>>
>>
>> wrote:
>>  > Well, continuing down this somewhat bizarre path:
>>  >
>>  > new_songs, old_songs = [], []
>>  > itertools.takewhile(
>>  >     lambda x: True,
>>  >     (new_songs if s.is_new() else old_songs).append(s) for s in songs)
>>  >     )
>>  >
>>  > I'm not sure I got the syntax exactly right, but the idea is anything
>>  > that will iterate over a generator expression.  That at least gets rid
>>  > of the memory requirement to hold the throw-away list :-)
>>
>> You could equivalently pass the generator to deque() with maxlen=0 -
>> this consumes the iterator with constant memory usage.
>
>
> any((new_songs if s.is_new() else old_songs).append(s) for s in songs)
>
>
> --
> http://mail.python.org/mailman/listinfo/python-list



-- 
Fábio Santos

[toc] | [prev] | [next] | [standalone]


#47829

FromTerry Reedy <tjreedy@udel.edu>
Date2013-06-12 14:47 -0400
Message-ID<mailman.3118.1371062857.3114.python-list@python.org>
In reply to#47716
On 6/12/2013 12:57 PM, Fábio Santos wrote:
> Why is there no builtin to consume a generator? I find that odd.

There are several builtins than consume generators -- and do something 
useful with the yielded objects. What you mean is "Why is there no 
builtin to uselessly consume a generator?" The question almost answers 
itself. A generator generates objects to be used.

> On Wed, Jun 12, 2013 at 5:28 PM, Serhiy Storchaka <storchaka@gmail.com> wrote:

>> 12.06.13 09:32, Phil Connell написав(ла):
>>> You could equivalently pass the generator to deque() with maxlen=0 -
>>> this consumes the iterator with constant memory usage.

>> any((new_songs if s.is_new() else old_songs).append(s) for s in songs)

The problem here is that the generator generates and yields an unwanted 
sequence of None (references) from the append operations. The proper 
loop statement

for s in songs:
     (new_songs if s.is_new() else old_songs).append(s)

simply ignores the None return of the appends. Since it does not yield 
None over and over, no extra code is needed to ignore what should be 
ignored in the first place.

-- 
Terry Jan Reedy

[toc] | [prev] | [next] | [standalone]


#47929

FromOscar Benjamin <oscar.j.benjamin@gmail.com>
Date2013-06-13 10:43 +0100
Message-ID<mailman.3178.1371116623.3114.python-list@python.org>
In reply to#47716
On 12 June 2013 19:47, Terry Reedy <tjreedy@udel.edu> wrote:
> The proper loop statement
>
> for s in songs:
>     (new_songs if s.is_new() else old_songs).append(s)

I think I would just end up rewriting this as

for s in songs:
    if s.is_new():
        new_songs.append(s)
    else:
        old_songs.append(s)

but then we're back where we started. I don't think any of the
solutions posted in this thread have been better than this. If you
want to make this a nice one-liner then just put this code in a
function.


Oscar

[toc] | [prev] | [next] | [standalone]


#47614

FromChris Rebert <clp2@rebertia.com>
Date2013-06-10 16:03 -0700
Message-ID<mailman.2994.1370905425.3114.python-list@python.org>
In reply to#47603
On Mon, Jun 10, 2013 at 1:34 PM, Roy Smith <roy@panix.com> wrote:
> I have a list, songs, which I want to divide into two groups.
> Essentially, I want:
>
> new_songs = [s for s in songs if s.is_new()]
> old_songs = [s for s in songs if not s.is_new()]
>
> but I don't want to make two passes over the list.  I could do:
>
> new_songs = []
> old_songs = []
> for s in songs:
>     if s.is_new():
>         new_songs.append(s)
>     else:
>         old_songs.append(s)
>
> Which works, but is klunky compared to the two-liner above.  This
> seems like a common enough thing that I was expecting to find
> something in itertools which did this.  I'm thinking something along
> the lines of:
>
> matches, non_matches = isplit(lambda s: s.is_new, songs)
>
> Does such a thing exist?

itertools.groupby() is kinda similar, but unfortunately doesn't fit
the bill due to its sorting requirement.
There is regrettably no itertools.partition(). And given how dead-set
Raymond seems to be against adding things to the itertools module,
there will likely never be.
Maybe more-itertools (https://pypi.python.org/pypi/more-itertools )
would accept a patch?

Cheers,
Chris

[toc] | [prev] | [next] | [standalone]


#47615

FromTim Chase <python.list@tim.thechases.com>
Date2013-06-10 18:10 -0500
Message-ID<mailman.2995.1370905719.3114.python-list@python.org>
In reply to#47603
On 2013-06-11 08:50, Chris Angelico wrote:
> The iterator version strikes my fancy. Maybe this isn't of use to
> you, but I'm going to try my hand at making one anyway.
> 
> >>> def iterpartition(pred,it):
> 	"""Partition an iterable based on a predicate.
> 
> 	Returns two iterables, for those with pred False and those
> True.""" falses,trues=[],[]

This is really nice.  I think the only major modification I'd make is
to use a collections.deque() instead of lists here:

  trues, falses = collections.deque(), collections.deque()

as I seem to recall that list.pop(0) has some performance issues if
it gets long, while the deque is optimized for fast (O(1)?) push/pop
on either end.

-tkc


[toc] | [prev] | [next] | [standalone]


#47616

FromFábio Santos <fabiosantosart@gmail.com>
Date2013-06-11 00:08 +0100
Message-ID<mailman.2996.1370905734.3114.python-list@python.org>
In reply to#47603

[Multipart message — attachments visible in raw view] — view raw

On 10 Jun 2013 23:54, "Roel Schroeven" <roel@roelschroeven.net> wrote:
>
> You could do something like:
>
> new_songs, old_songs = [], []
> [(new_songs if s.is_new() else old_songs).append(s) for s in songs]
>
> But I'm not sure that that's any better than the long version.

This is so beautiful!

[toc] | [prev] | [next] | [standalone]


#47717

Fromalex23 <wuwei23@gmail.com>
Date2013-06-11 17:44 -0700
Message-ID<51638894-039f-4449-a322-c3e16184efa3@ow4g2000pbc.googlegroups.com>
In reply to#47616
On Jun 11, 9:08 am, Fábio Santos <fabiosantos...@gmail.com> wrote:
> On 10 Jun 2013 23:54, "Roel Schroeven" <r...@roelschroeven.net> wrote:
> > new_songs, old_songs = [], []
> > [(new_songs if s.is_new() else old_songs).append(s) for s in songs]
>
> This is so beautiful!

No, it's actually pretty terrible. It creates a list in order to
populate _two other lists_ and then throws away the one made by the
comprehension. There's nothing ugly about multiline for-loops.

[toc] | [prev] | [next] | [standalone]


#47617

FromChris Angelico <rosuav@gmail.com>
Date2013-06-11 09:12 +1000
Message-ID<mailman.2997.1370905986.3114.python-list@python.org>
In reply to#47603
On Tue, Jun 11, 2013 at 9:10 AM, Tim Chase
<python.list@tim.thechases.com> wrote:
> On 2013-06-11 08:50, Chris Angelico wrote:
>> The iterator version strikes my fancy. Maybe this isn't of use to
>> you, but I'm going to try my hand at making one anyway.
>>
>> >>> def iterpartition(pred,it):
>>       """Partition an iterable based on a predicate.
>>
>>       Returns two iterables, for those with pred False and those
>> True.""" falses,trues=[],[]
>
> This is really nice.  I think the only major modification I'd make is
> to use a collections.deque() instead of lists here:
>
>   trues, falses = collections.deque(), collections.deque()
>
> as I seem to recall that list.pop(0) has some performance issues if
> it gets long, while the deque is optimized for fast (O(1)?) push/pop
> on either end.

Sure. If it's that good I might submit it to more-itertools... heh.

ChrisA

[toc] | [prev] | [next] | [standalone]


Page 1 of 2  [1] 2  Next page →

Back to top | Article view | comp.lang.python


csiph-web