Velocity Reviews > Packing list elements into tuples

# Packing list elements into tuples

Nickolay Kolev
Guest
Posts: n/a

 11-09-2004
Hi all,

I have a list whose length is a multiple of 3. I want to get a list of
tuples each of which has 3 consecutive elements from the original list,
thus packing the list into smaller packets. Like this:

l = [1,2,3,4,5,6]

tups = [(1,2,3), (4,5,6)]

or

l = [1,2,3,4,5,6,7,8,9]

tups = [(1,2,3), (4,5,6), (7,8,9)]

if i can dictionaries it would be even better:

l = [1,2,3,4,5,6]

tups = [
{'first':1,'second':2,'third':3},
{'first':4,'second':5,'third':6}
]

and so on.

Any ideas?

Cheers,
-- Nickolay

Russell Blau
Guest
Posts: n/a

 11-09-2004
"Nickolay Kolev" <(E-Mail Removed)> wrote in message
news:cmrcqq\$rv4\$(E-Mail Removed)-bonn.de...
> Hi all,
>
> I have a list whose length is a multiple of 3. I want to get a list of
> tuples each of which has 3 consecutive elements from the original list,
> thus packing the list into smaller packets. Like this:
>
> l = [1,2,3,4,5,6,7,8,9]
>
> tups = [(1,2,3), (4,5,6), (7,8,9)]

How's this? :

>>> alist

[1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> tups = [tuple(alist[i:i+3]) for i in xrange(0, len(alist), 3)]
>>> tups

[(1, 2, 3), (4, 5, 6), (7, 8, 9)]

>
> if i can dictionaries it would be even better:
>
>
> l = [1,2,3,4,5,6]
>
> tups = [
> {'first':1,'second':2,'third':3},
> {'first':4,'second':5,'third':6}
> ]

Well, now that you've been introduced to the concept of list indexes and
list comprehensions, I think that extending it to dictionaries is fairly
simple.

--
I don't actually read my hotmail account, but you can replace hotmail with
excite if you really want to reach me.

Nickolay Kolev
Guest
Posts: n/a

 11-09-2004
Nickolay Kolev wrote:

> Hi all,
>
> I have a list whose length is a multiple of 3. I want to get a list of
> tuples each of which has 3 consecutive elements from the original list,
> thus packing the list into smaller packets. Like this:

I found it in the cookbook:
http://aspn.activestate.com/ASPN/Coo.../Recipe/303060

It looks quite cryptic, could anyone give me some pointers at what
exactly is going on in the function?

Cheers,
-- Nickolay

Steven Bethard
Guest
Posts: n/a

 11-09-2004
Nickolay Kolev <nmkolev <at> uni-bonn.de> writes:
> l = [1,2,3,4,5,6]
>
> tups = [(1,2,3), (4,5,6)]

My favorite idiom:

>>> l = [1,2,3,4,5,6]
>>> zip(*(iter(l),)*3)

[(1, 2, 3), (4, 5, 6)]
>>> l = [1,2,3,4,5,6,7,8,9]
>>> zip(*(iter(l),)*3)

[(1, 2, 3), (4, 5, 6), (7, 8, 9)]

If you want dicts, try:

>>> l = [1,2,3,4,5,6]
>>> tuples = zip(*(iter(l),)*3)
>>> labels = 'first second third'.split()
>>> [dict(zip(labels, t)) for t in tuples]

[{'second': 2, 'third': 3, 'first': 1}, {'second': 5, 'third': 6, 'first': 4}]

Basically, you can make a dict with whatever labels you want by zipping your
labels with the appropriate tuples and calling the dict builtin.

Steve

Scott David Daniels
Guest
Posts: n/a

 11-09-2004
I kind of like:
[dict(first=lst[k], second=lst[k+1], third=lst[k+2])
for k in range(0, len(lst), 3)]

--Scott David Daniels
http://www.velocityreviews.com/forums/(E-Mail Removed)

Thorsten Kampe
Guest
Posts: n/a

 11-10-2004
* Nickolay Kolev (2004-11-09 22:29 +0100)
> I have a list whose length is a multiple of 3. I want to get a list of
> tuples each of which has 3 consecutive elements from the original list,
> thus packing the list into smaller packets. Like this:
>
> l = [1,2,3,4,5,6]
>
> tups = [(1,2,3), (4,5,6)]
>
> or
>
> l = [1,2,3,4,5,6,7,8,9]
>
> tups = [(1,2,3), (4,5,6), (7,8,9)]

Fragment[1] from a general purpose partitioning utility:

def part(seq, slice):
""" partition seq """
return [seq[slice * index:slice * (index + 1)]
for index in range(len(seq) / slice + bool(len(seq) % slice))]

> if i can dictionaries it would be even better:
>
> l = [1,2,3,4,5,6]
>
> tups = [
> {'first':1,'second':2,'third':3},
> {'first':4,'second':5,'third':6}
> ]

You're losing order and therfore you're attaching 'first', 'second'
and so on because you obviously need the original order. So don't use
a dictionary.

Thorsten

[1] Just for the record:
def part(seq, slice = None, modus = None):
"""
partition seq
syntax:
part(seq, boolean_function, modus = 'bool')
-> [[first_true_items], [first_false_item, remaining_items]]
part('str', 'separator', modus = 'sep') or
part('str', ['separators'], modus = 'sep')
part(list, item, modus = 'sep')
part(n, modus = 'set')
-> len([all_possible_partitions_of_[0, 1, ..., n]])
part(list, modus = 'set'
-> [all_possible_partitions_of_list]
part(seq, int, modus = 'size')
-> [seq0, seq1, ..., seqn] - where len(seq(i)) = int
part(seq, [n0, n1, ..., n(i)], modus = 'size'
-> [seq0, seq1, ..., seq(i)] - where len(seq(i)) = n(i)
"""

Peter Otten
Guest
Posts: n/a

 11-10-2004
Nickolay Kolev wrote:

> I have a list whose length is a multiple of 3. I want to get a list**of
> tuples each of which has 3 consecutive elements from the original list,
> thus packing the list into smaller packets. Like this:

> ... dictionaries ... would be even better:

> l = [1,2,3,4,5,6]
>
> tups = [
> {'first':1,'second':2,'third':3},
> {'first':4,'second':5,'third':6}
> ]

itertools to the rescue:

>>> from itertools import *
>>> names = "first second third".split()
>>> items = range(6)
>>> map(dict, takewhile(bool, starmap(zip, repeat((names, iter(items))))))

[{'second': 1, 'third': 2, 'first': 0}, {'second': 4, 'third': 5, 'first':
3}]

Fun, but not recommended. There must be a simpler expression where you need
not mention len(names) explicitly - but right now I can't think of one.
Note how the nonzero len(items) % len(names) case is handled:

>>> map(dict, takewhile(bool, starmap(zip, repeat((names,

iter(range(5)))))))
[{'second': 1, 'third': 2, 'first': 0}, {'second': 4, 'first': 3}]

However, in real life I would rather go with Steven Bethard's two-step
approach...

Peter

Russell Blau
Guest
Posts: n/a

 11-10-2004
"Nickolay Kolev" <(E-Mail Removed)> wrote in message
news:cmreif\$rve\$(E-Mail Removed)-bonn.de...
> Nickolay Kolev wrote:
>
> I found it in the cookbook:
> http://aspn.activestate.com/ASPN/Coo.../Recipe/303060
>
> It looks quite cryptic, could anyone give me some pointers at what
> exactly is going on in the function?

The function contains only one significant line, which is:

return zip(*[lst[i::n] for i in range(n)])

To figure out what it is doing, take it one piece at a time. Start with a
list:

>>> lst

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]

Pick how many elements you want in each group:

>>> n=3

Now see what the list comprehension does:

>>> [lst[i::n] for i in range(n)]

[[1, 4, 7, 10], [2, 5, 8, 11], [3, 6, 9, 12]]

So this breaks up the list into three separate lists by taking each n'th
element from the original list, then combines them. The "n" in lst[i::n]
determines how many elements will be skipped in creating each sub-list.
Unfortunately, this syntax isn't mentioned in the tutorial or the library
reference; you have to dig to find an explanation of it in
http://www.python.org/doc/2.3.4/what...on-slices.html

Let's store the result of the last step so we can see what the following
step does:

>>> m = [lst[i::n] for i in range(n)]
>>> zip(*m)

[(1, 2, 3), (4, 5, 6), (7, 8, 9), (10, 11, 12)]

The magic function zip() takes a list of sequences as its argument, and
returns a new list that rearranges them. Imagine the first list (m) as a
two-dimensional table in which each sub-sequence is a row; zip() goes
through the table and returns the columns as the sub-sequences. See
http://www.python.org/doc/2.3.4/lib/built-in-funcs.html (all the way at the
end).

--
I don't actually read my hotmail account, but you can replace hotmail with
excite if you really want to reach me.

Steven Bethard
Guest
Posts: n/a

 11-10-2004
On Wed, 10 Nov 2004 19:00:52 +0800, Deepak Sarda <(E-Mail Removed)> wrote:
>
> > >>> l = [1,2,3,4,5,6]
> > >>> zip(*(iter(l),)*3)

> > [(1, 2, 3), (4, 5, 6)]
> > >>> l = [1,2,3,4,5,6,7,8,9]
> > >>> zip(*(iter(l),)*3)

> > [(1, 2, 3), (4, 5, 6), (7, 8, 9)]

>
> Can someone explain how this works?!
>

[snip]
>
> So zip() basically gets zip(iter(l), iter(l), iter(l)) , right?

Close, but note that zip(iter(l), iter(l), iter(l)) creates three
iterators to the list l, while zip(*(iter(l),)*3) uses the same
iterator at each position in the tuple.

>>> l = [1,2,3,4,5,6]
>>> zip(iter(l), iter(l), iter(l))

[(1, 1, 1), (2, 2, 2), (3, 3, 3), (4, 4, 4), (5, 5, 5), (6, 6, 6)]
>>> itr = iter(l)
>>> zip(itr, itr, itr)

[(1, 2, 3), (4, 5, 6)]

Here's basically what zip ends up doing when you give it 3 names bound
to the same iterator:

>>> itr = iter(l)
>>> tuple1 = (itr.next(), itr.next(), itr.next())
>>> tuple1

(1, 2, 3)
>>> tuple2 = (itr.next(), itr.next(), itr.next())
>>> tuple2

(4, 5, 6)
>>> result = [tuple1, tuple2]
>>> result

[(1, 2, 3), (4, 5, 6)]

Note that when they're not the same iterator, zip does something like:

>>> itr1, itr2, itr3 = iter(l), iter(l), iter(l)
>>> tuple1 = (itr1.next(), itr2.next(), itr3.next())
>>> tuple1

(1, 1, 1)
>>> tuple2 = (itr1.next(), itr2.next(), itr3.next())
>>> tuple2

(2, 2, 2)
....

So you just get 3 copies of the elements at each index in your list.

Hope that helped!

Steve
--
You can wordify anything if you just verb it.
- Bucky Katt, Get Fuzzy