简体   繁体   中英

How do you cycle a Python list to extend it from M to N values (M < N)?

I have a list of elements, N, that I want to run through repeatedly until it has M elements, where M can be any integer, not necessarily an integer multiple of N.

I found two ways to do this but neither seems as direct as I would expect is possible. Is there a more native way to do this in Python?

Here is what I came up with. Note that the first method was faster in limited testing.

from math import ceil

def repeat_list_1(values, n):
    repeated = values * ceil(n / len(values))
    return repeated[:n]
from itertools import cycle

def repeat_list_2(values, n):
    values_cycler = cycle(values)
    repeated = [next(values_cycler) for _ in range(n)]
    return repeated
values = list(range(5))
n = 12
repeated1 = repeat_list_1(values, n)
repeated2 = repeat_list_2(values, n)
assert repeated1 == repeated2
print(repeated1)
[0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 0, 1, 2]

As already stated, there are multiple ways to achieve that, you have to pick your favorite one based on some criteria (speed, code simplicity, ...).

Here's one that I find simple (although it's old style - it doesn't use generators):

 >>> def repeat_list_4(values, n): ... d, m = divmod(n, len(values))... return values * d + values[:m]... >>> >>> repeat_list_4(list(range(5)), 12) [0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 0, 1] >>> repeat_list_4(list(range(5)), 5) [0, 1, 2, 3, 4] >>> repeat_list_4(list(range(5)), 1) [0] >>> repeat_list_4(list(range(5)), 0) []

As a side note, in your example there's n = 12 , but the resulting list has 13 elements.

You can use islice to get the first n values of an iterable (called take in some other languages):

from itertools import cycle, islice

def repeat_list_3(values, n):
    return list(islice(cycle(values), n))

I don't know whether this is efficient or not but you can do this.

l = [0, 1, 2, 3, 4]
N = 12
k = (l * (N // len(l) + len(l) - N % len(l)))[:N]

print(k)
# [0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 0, 1]

First you extend the list upto N + k number of terms where N + k is the factor of len(l) . Finally you slice the resulting list to get N elements.

numpy has a handy function:

np.resize(values, n)

but you would need to convert it back from np.ndarray to list

I've done this a couple of times using zip() to define a limit while iterating cyclically through a list.

An example that uses itertools:

from itertools import cycle


values = ['a', 'b', 'c', 1]
n = 10

pool = cycle(values)

for item, limit in zip(pool, range(n)):
    print(item)

A way to do this without importing is to iterate cyclically without using itertools, which can be done easily using a generator.

values = ['a', 'b', 'c', 1]

def circular():
    while True:
        for connection in values:
            yield connection

n = 10

for item, limit in zip(circular(), range(n)):
    print(item)

I do think however that this solution is clunky and doesn't use zip() as it is intended to be used.

I also took the code from your second example and replaced cycle with a generator to avoid having to import anything.

values = ['a', 'b', 'c', 1]
n = 10

def circular():
    while True:
        for connection in values:
            yield connection


def repeat_list_2(values, n):
    values_cycler = circular()
    repeated = [next(values_cycler) for _ in range(n)]
    return repeated

print(repeat_list_2(values, n))

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