views:

3573

answers:

8

List Comprehension for me seems to be like the opaque block of granite that regular expressions are for me. I need pointers.

Say, I have a 2D list:

li = [[0,1,2],[3,4,5],[6,7,8]]

I would like to merge this either into one long list

li2 = [0,1,2,3,4,5,6,7,8]

or into a string with separators:

s = "0,1,2,3,4,5,6,7,8"

Really, I'd like to know how to do both.

A: 
import itertools
itertools.flatten( li )
There is no itertools.flatten.
Antti Rasinen
I think he's talking about the example function shown in the itertools docs:http://docs.python.org/library/itertools.htmlEither way, not list-comprehension-based.
Peter Hosey
A: 

To make it a flattened list use either:

  1. http://code.activestate.com/recipes/121294/
  2. http://code.activestate.com/recipes/363051/

Then, join to make it a string.

Eli Bendersky
I don't see a flatten function in itertools. I am using Python 2.4.2
Arthur Thomas
+5  A: 

There's a couple choices. First, you can just create a new list and add the contents of each list to it:

li2 = []
for sublist in li:
    li2.extend(sublist)

Alternately, you can use the itertools module's chain function, which produces an iterable containing all the items in multiple iterables:

import itertools
li2 = list(itertools.chain(*li))

If you take this approach, you can produce the string without creating an intermediate list:

s = ",".join(itertools.chain(*li))
Allen
The idea is good, but it crashes because you did not take car of the data type : <type 'exceptions.TypeError'>: sequence item 0: expected string, int found
e-satis
Easily solved: ','.join(itertools.imap(str, itertools.chain(*li)))
Peter Hosey
A: 

For the second one, there is a built-in string method to do that :

>>> print ','.join(str(x) for x in li2)
"0,1,2,3,4,5,6,7,8"

For the first one, you can use join within a comprehension list :

>>> print ",".join([",".join(str(x) for x in li])
"0,1,2,3,4,5,6,7,8"

But it's easier to use itertools.flatten :

>>> import itertools
>>> print itertools.flatten(li)
[0,1,2,3,4,5,6,7,8]
>>> print ",".join(str(x) for x in itertools.flatten(li))
"0,1,2,3,4,5,6,7,8"

N.B : itertools is a module that help you to deal with common tasks with iterators such as list, tuples or string... It's handy because it does not store a copy of the structure you're working on but process the items one by one.

EDIT : funny, I am learning plenty of way to do it. Who said that there was only one good way to do it ?

e-satis
Which version of Python are you using that has itertools.flatten? It's not part of the standard library as far as I can see.Also, the saying is "one -obvious- way", which is quite different from "good".
Matthew Trevor
+3  A: 

My favorite, and the shortest one, is this:

li2 = sum(li, [])

and

s = ','.join(li2)

EDIT: use sum instead of reduce, (thanks Thomas Wouters!)

dF
This approach is easier to do with sum: sum(li, [])
Thomas Wouters
This does not work : <type 'exceptions.TypeError'>: reduce() arg 2 must support iteration
e-satis
+12  A: 

Like so:

[ item for innerlist in outerlist for item in innerlist ]

Turning that directly into a string with separators:

','.join(str(item) for innerlist in outerlist for item in innerlist)

Yes, the order of 'for innerlist in outerlist' and 'for item in innerlist' is correct. Even though the "body" of the loop is at the start of the listcomp, the order of nested loops (and 'if' clauses) is still the same as when you would write the loop out:

for innerlist in outerlist:
    for item in innerlist:
        ...
Thomas Wouters
Thank you! That ordering has never made any sense to me until you explained it that way.
Nicholas Riley
+4  A: 

Try that:

li=[[0,1,2],[3,4,5],[6,7,8]]
li2 = [ y for x in li for y in x]

You can read it like this:
Give me the list of every ys.
The ys come from the xs.
The xs come from li.

To map that in a string:

','.join(map(str,li2))
Martin Cote
A: 

There are many ways to do this problem. I like Numpy's tools because it is normally already imported in everything I do. However, if you aren't using Numpy for anything else this probably isn't a good method.

import numpy
li = [[0,1,2],[3,4,5],[6,7,8]]
li2=li[0] #first element of array to merge
i=1 
while i<len(li):
    li2=numpy.concatenate((li2,li[i]))
    i+=1
print li2

This would print [0 1 2 3 4 5 6 7 8] and then you can convert this into your string too.

Alex