简体   繁体   中英

Itertools Chain on Nested List

I have two lists combined sequentially to create a nested list with python's map and zip funcionality; however, I wish to recreate this with itertools.

Furthermore, I am trying to understand why itertools.chain is returning a flattened list when I insert two lists, but when I add a nested list it simply returns the nested list.

Any help on these two issues would be greatly appreciated.

from itertools import chain

a = [0,1,2,3]
b = [4,5,6,7]

#how can I produce this with itertools?
c = list(map(list, zip(a,b)))
print(c) #[[0, 4], [1, 5], [2, 6], [3, 7]]

d = list(chain(c))
print(d) #[[0, 4], [1, 5], [2, 6], [3, 7]]

d = list(chain(a,b))
print(d) #[0, 1, 2, 3, 4, 5, 6, 7]

I'll try to answer your questions as best I can.

First off, itertools.chain doesn't work the way you think it does. chain takes x number of iterables and iterates over them in sequence. When you call chain , it essentially (internally) packs the objects into a list:

chain("ABC", "DEF") # Internally creates ["ABC", "DEF"]

Inside the method, it accesses each of these items one at a time, and iterates through them:

for iter_item in arguments:
    for item in iter_item:
        yield item

So when you call chain([[a,b],[c,d,e],[f,g]]) , it creates a list with one iterable object: the list you passed as an argument. So now it looks like this:

[ #outer
    [ #inner
        [a,b],
        [c,d,e],
        [f,g]
    ]
]

chain as such iterates over the inner list, and returns three elements: [a,b] , [c,d,e] , and [f,g] in order. Then they get repacked by list , giving you what you had in the first place.

Incidentally, there is a way to do what you want to: chain.from_iterable . This is an alternate constructor for chain which accepts a single iterable, such as your list, and pulls the elements out to iterate over. So instead of this:

# chain(l)
[ #outer
    [ #inner
        [a,b],
        [c,d,e],
        [f,g]
    ]
]

You get this:

# chain.from_iterable(l)
[
    [a,b],
    [c,d,e],
    [f,g]
]

This will iterate through the three sub-lists, and return them in one sequence, so list(chain.from_iterable(l)) will return [a,b,c,d,e,f,g] .

As for your second question: While I don't know why itertools is a necessity to this process, you can do this in Python 2.x:

list(itertools.izip(x,y))

However, in 3.x, the izip function has been removed. There is still zip_longest , which will match up as many pairs as it can, and accept a filler value for extra pairs: list(zip_longest([a,b,c],[d,e,f,g,h],fillvalue="N")) returns [(a,d),(b,e),(c,f),(N,g),(N,h)] since the second list is longer than the first. Normal zip will take the shortest iterable and cut off the rest.

In other words, unless you want zip_longest instead of zip , itertools does not have a built-in method for zipping.

You can also run itertools.chain(*your_list_of_lists) . For example:

for p in itertools.chain(*[[1,2],[3,4]]):
    print(p)
1
2
3
4

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM