Converting a flat list to a list of tuples

B

bonono

Bengt said:
Laurent Rahuel said:
Hi,

newList = zip(aList[::2], aList[1::2])
newList
[('a', 1), ('b', 2), ('c', 3)]

Regards,

Laurent

Or if aList can get very large and/or the conversion has to be
performed many times:

from itertools import islice
newList = zip(islice(aList,0,None,2), islice(aList,1,None,2))
Or, if you want to include fractional groups at the end
aList = ['a', 1, 'b', 2, 'c', 3]
from itertools import groupby
def grouper(n):
... def git():
... while True:
... for _ in xrange(n): yield 0
... for _ in xrange(n): yield 1
... git = git()
... def grouper(_): return git.next()
... return grouper
...
[tuple(g) for _, g in groupby(aList, grouper(2))] [('a', 1), ('b', 2), ('c', 3)]
[tuple(g) for _, g in groupby(aList, grouper(3))] [('a', 1, 'b'), (2, 'c', 3)]
[tuple(g) for _, g in groupby(aList, grouper(4))]
[('a', 1, 'b', 2), ('c', 3)]
Personally, I would like to see it as [('a',1,'b',2), ('c',3,
None,None)], as a list of tuple of equal length is easier to be dealt
with.

i = iter(aList)
zip(i,chain(i,repeat(None)),
chain(i,repeat(None)),chain(i,repeat(None)))
 
B

Bengt Richter

Bengt said:
:

Hi,

newList = zip(aList[::2], aList[1::2])
newList
[('a', 1), ('b', 2), ('c', 3)]

Regards,

Laurent

Or if aList can get very large and/or the conversion has to be
performed many times:

from itertools import islice
newList = zip(islice(aList,0,None,2), islice(aList,1,None,2))
Or, if you want to include fractional groups at the end
aList = ['a', 1, 'b', 2, 'c', 3]
from itertools import groupby
def grouper(n):
... def git():
... while True:
... for _ in xrange(n): yield 0
... for _ in xrange(n): yield 1
... git = git()
... def grouper(_): return git.next()
... return grouper
...
[tuple(g) for _, g in groupby(aList, grouper(2))] [('a', 1), ('b', 2), ('c', 3)]
[tuple(g) for _, g in groupby(aList, grouper(3))] [('a', 1, 'b'), (2, 'c', 3)]
[tuple(g) for _, g in groupby(aList, grouper(4))]
[('a', 1, 'b', 2), ('c', 3)]
Personally, I would like to see it as [('a',1,'b',2), ('c',3,
None,None)], as a list of tuple of equal length is easier to be dealt
with.

i = iter(aList)
zip(i,chain(i,repeat(None)),
chain(i,repeat(None)),chain(i,repeat(None)))
Yes, but OTOH you might like to loop and catch ValueError when/if the last tuple
doesn't unpack properly. Then you don't have to worry about None vs a sentinel :)

Regards,
Bengt Richter
 
B

bonono

Bengt said:
Bengt said:
:

Hi,

newList = zip(aList[::2], aList[1::2])
newList
[('a', 1), ('b', 2), ('c', 3)]

Regards,

Laurent

Or if aList can get very large and/or the conversion has to be
performed many times:

from itertools import islice
newList = zip(islice(aList,0,None,2), islice(aList,1,None,2))

Or, if you want to include fractional groups at the end

aList = ['a', 1, 'b', 2, 'c', 3]
from itertools import groupby
def grouper(n):
... def git():
... while True:
... for _ in xrange(n): yield 0
... for _ in xrange(n): yield 1
... git = git()
... def grouper(_): return git.next()
... return grouper
...
[tuple(g) for _, g in groupby(aList, grouper(2))]
[('a', 1), ('b', 2), ('c', 3)]
[tuple(g) for _, g in groupby(aList, grouper(3))]
[('a', 1, 'b'), (2, 'c', 3)]
[tuple(g) for _, g in groupby(aList, grouper(4))]
[('a', 1, 'b', 2), ('c', 3)]
Personally, I would like to see it as [('a',1,'b',2), ('c',3,
None,None)], as a list of tuple of equal length is easier to be dealt
with.

i = iter(aList)
zip(i,chain(i,repeat(None)),
chain(i,repeat(None)),chain(i,repeat(None)))
Yes, but OTOH you might like to loop and catch ValueError when/if the last tuple
doesn't unpack properly. Then you don't have to worry about None vs a sentinel :)
That to me is a matter of style. I in general prefer the no
catch/except way of coding, the filter/map way(or the unix pipe way).
Not that one is better than another.

But I think your solution for this case is a bit hard to swallow. I
would rather do it the more imperative way of :

def split(s,n):
a=[]
c=0
for x in s:
a.append(x)
c+=1
if c%n == 0:
yield a
a=[]
if a !=[]: yield a

list(split(aList,4))
 
D

Duncan Booth

Bengt said:
Duncan said:
it = iter(aList)
zip(it, it)
[('a', 1), ('b', 2), ('c', 3)]
is "relying on undefined behaviour" perhaps the new black ?
Is it really undefined? If so, IMO it should be defined to
do what it apparently does.
Hm, actually, something tells me I've seen some variation of this
before, but I can't think of the context off hand.
Yes, the subject does come up occasionally. Perhaps you are thinking
of this thread:

http://groups.google.co.uk/group/co...read/thread/83baa4bd42fc9b69/d933c7333d3863ce

In that thread, I was the one arguing that the behaviour was undefined.
My memory was that I was forced to back down on that one, but looking back
at the thread I now think it was only itertools.izip I was forced to admit
defines its behaviour as working that way.

More googling will show that it was proposed that zip should
be defined as working with this, but that proposal was rejected. See:

http://groups.google.co.uk/group/co...read/thread/a6ba37b0fb0fa69e/f8a3d3b6d1a9fcbd

So scratch my original suggestion and substitute this for defined behaviour:
[('a', 1), ('b', 2), ('c', 3)]
 
F

Fredrik Lundh

Bengt said:
That would be a counter-intuitive thing to do. Most things go left->right
in order as the default assumption.

it's not only the order that matters, but also the number of items
read from the source iterators on each iteration.

</F>
 
P

Peter Otten

Personally, I would like to see it as [('a',1,'b',2), ('c',3,
None,None)],  as a list of tuple of equal length is easier to be dealt
with.

i = iter(aList)
zip(i,chain(i,repeat(None)),
chain(i,repeat(None)),chain(i,repeat(None)))

Here's some more:
[(0, 1), (2, 3), (4, None)]
[(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 'MISSING', 'MISSING')]

Peter
 
B

Bengt Richter

it's not only the order that matters, but also the number of items
read from the source iterators on each iteration.
Not sure I understand.

Are you thinking of something like lines from a file, where there might be
chunky buffering? ISTM that wouldn't matter if the same next method was called.
Here we have multiple references to the same iterator. Isn't e.g. buiding
a plain tuple defined with evaluation one element at a time left to right?
So an iterator it = xrange(4) can't know that it's being used in a context
like (it.next(), it.next()), so why should zip be any different? Zip _is_ building
tuples after all, and it's perfectly clear where they are coming from (or am
I missing something?) Why not left to right like a normal tuple?

Regards,
Bengt Richter
 
B

bonono

Bengt said:
Not sure I understand.

Are you thinking of something like lines from a file, where there might be
chunky buffering? ISTM that wouldn't matter if the same next method was called.
Here we have multiple references to the same iterator. Isn't e.g. buiding
a plain tuple defined with evaluation one element at a time left to right?
So an iterator it = xrange(4) can't know that it's being used in a context
like (it.next(), it.next()), so why should zip be any different? Zip _is_ building
tuples after all, and it's perfectly clear where they are coming from (or am
I missing something?) Why not left to right like a normal tuple?
The implementor of zip() may select to buffer the iterables so instead
of it.next(), it may loop it for a number of tmes, or emit multiple
threads making it async and all those kind of thing.

However, I would say this is highly unlikely or like a extremely remote
scenario to prove that this usage is wrong and we are bad boys ;-)
 
F

Fredrik Lundh

Bengt said:
Are you thinking of something like lines from a file, where there might be
chunky buffering? ISTM that wouldn't matter if the same next method was called.
Here we have multiple references to the same iterator. Isn't e.g. buiding
a plain tuple defined with evaluation one element at a time left to right?

yeah, but what says that the iterator has to be called during tuple construction?

while 1:
for each sequence:
# optimize cache behaviour!
grab up to N items from each iterator
M = length of shortest output list
for i in range(M):
build tuple and append
if M != N:
break

</F>
 
P

Peter Otten

Fredrik said:
yeah, but what says that the iterator has to be called during tuple
construction?

while 1:
for each sequence:
# optimize cache behaviour!
grab up to N items from each iterator
M = length of shortest output list
for i in range(M):
build tuple and append
if M != N:
break

Wouldn't every attempt to introduce such an optimization be shot down by the
likes of

def exponential():
for i in xrange(sys.maxint):
time.sleep(2**i)
yield "whatever"

def const():
for i in xrange(5): yield i

zip(exponential(), const())

To say it another way, aren't the problems that can be created by not
specifying zip() behaviour in a way that allows the zip(it, it) trick worse
than those you want to prevent?

Peter
 
B

Bengt Richter

yeah, but what says that the iterator has to be called during tuple construction?

while 1:
for each sequence:
# optimize cache behaviour!
grab up to N items from each iterator
M = length of shortest output list
for i in range(M):
build tuple and append
if M != N:
break
I'd say it's ok to cache some iterator output, but the cache buffer
must be associated with the iterator like cachedict[id(it)]. Then
the loop would only pump up the same cache buffer, since all iterators
would refer to the same buffer, and the loop that builds the tuple would
draw one from "each" buffer unknowingly drawing from the same buffer,
since they would be all found by cachedict[id(it)] which would be the same.
This would preserve the identity and logical sequentiality of the data
stream(s). To buffer separately on the unverified assumption that they
are different iterators seems like a buggy implementation to me ;-)
(optimizing use of total available buffering space is another matter,
not to mention handling StopIteration in the buffer loading)

ISTM to me the easiest just to define the intuitive behaviour, and let
implementers buffer accordingly if they want to.

Regards,
Bengt Richter
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,483
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top