A.K.
A.K.

Reputation: 141

Attempting Python list comprehension with two variable of different ranges

I'm trying to generate a list quickly with content from two different arrays of size n and n/2. As an example:

A = [70, 60, 50, 40, 30, 20, 10, 0]
B = [1, 2, 3, 4]

I wish to generate something like

[(A[x], B[y]) for x in range(len(A)) for y in range(len(B))]

I understand the second for statement is the nested for loop after the "x" one. I'm trying to get the contents of the new array to be

A[0], B[0]
A[1], B[1]
A[2], B[2]
A[3], B[3]
A[4], B[0]
A[5], B[1]
A[6], B[2]
A[7], B[3]

Could anyone point me in the right direction?

Upvotes: 13

Views: 2470

Answers (3)

Martijn Pieters
Martijn Pieters

Reputation: 1122232

Don't use nested loops; you are pairing up A and B, with B repeating as needed. What you need is zip() (to do the pairing), and itertools.cycle() (to repeat B):

from itertools import cycle

zip(A, cycle(B))

If B is always going to be half the size of A, you could also just double B:

zip(A, B + B)

Demo:

>>> from itertools import cycle
>>> A = [70, 60, 50, 40, 30, 20, 10, 0]
>>> B = [1, 2, 3, 4]
>>> zip(A, cycle(B))
[(70, 1), (60, 2), (50, 3), (40, 4), (30, 1), (20, 2), (10, 3), (0, 4)]
>>> zip(A, B + B)
[(70, 1), (60, 2), (50, 3), (40, 4), (30, 1), (20, 2), (10, 3), (0, 4)]

For cases where it is not known which one is the longer list, you could use min() and max() to pick which one to cycle:

zip(max((A, B), key=len), cycle(min((A, B), key=len))

or for an arbitrary number of lists to pair up, cycle them all but use itertools.islice() to limit things to the maximum length:

inputs = (A, B)  # potentially more
max_length = max(len(elem) for elem in inputs)
zip(*(islice(cycle(elem), max_length) for elem in inputs))

Demo:

>>> from itertools import islice
>>> inputs = (A, B)  # potentially more
>>> max_length = max(len(elem) for elem in inputs)
>>> zip(*(islice(cycle(elem), max_length) for elem in inputs))
[(70, 1), (60, 2), (50, 3), (40, 4), (30, 1), (20, 2), (10, 3), (0, 4)]

Upvotes: 21

zondo
zondo

Reputation: 20336

[(A[x % len(A)], B[x % len(B)]) for x in range(max(len(A), len(B)))]

This will work whether or not A is the larger list. :)

Upvotes: 10

John Howard
John Howard

Reputation: 64135

Try using only one for loop instead of two and having the second wrap back to 0 once it gets past its length.

[(A[x], B[x%len(B)]) for x in range(len(A))]

Note that this will only work if A is the longer list. If you know B will always be half the size of A you can also use this:

list(zip(A, B*2))

Upvotes: 5

Related Questions