4

OK so I want to create within a loop iterator in index that goes like this

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

where the maximum value may be 8 or another number.

so far I have some code that doesn't work.

a = ['l','m','n','o','p','q','r','s','t'] #len(a) = 9
b = [[]] *len(a)
c = [[]] *len(a)
for offset_index in range(len(a)):
    b[offset_index] = []
    c[offset_index] = []
    for i in range(offset_index, len(a) - offset_index, 1):
        b[offset_index].append(i)
        c[offset_index].append(a[i])
print b

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

I can see it's a problem with the 2nd range function, but can't imagine a neat fix.

cs95
  • 274,032
  • 76
  • 480
  • 537
Joylove
  • 384
  • 3
  • 19

5 Answers5

3

itertools.cycle + itertools.islice

Efficient cycling and iteration — no copies were harmed in the process of writing this solution.

from itertools import cycle, islice

l = list(range(9))
repeats = 8

[list(islice(cycle(l), i, len(l) + i)) for i in range(repeats)]

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

Or, if you don't want to preserve the indices:

for i in range(repeats):
    idx = list(islice(cycle(l), i, len(l) + i)) 
    ... # do something with idx

List Comprehension

The choice of users looking for performance.

[l[i:] + l[:i] for i in range(repeats)]

[[0, 1, 2, 3, 4, 5, 6, 7, 8],
 [1, 2, 3, 4, 5, 6, 7, 8, 0],
 [2, 3, 4, 5, 6, 7, 8, 0, 1],
 [3, 4, 5, 6, 7, 8, 0, 1, 2],
 [4, 5, 6, 7, 8, 0, 1, 2, 3],
 [5, 6, 7, 8, 0, 1, 2, 3, 4],
 [6, 7, 8, 0, 1, 2, 3, 4, 5],
 [7, 8, 0, 1, 2, 3, 4, 5, 6]]
Community
  • 1
  • 1
cs95
  • 274,032
  • 76
  • 480
  • 537
  • This is the best answer if I need to preserve the index for later. Thanks. – Joylove Feb 20 '18 at 03:42
  • @Joylove Are your arrays large? In that case, my option 1 may be more efficient than the other answers. I've added a version that does not preserve the indices. Check it out :) – cs95 Feb 20 '18 at 03:44
2

Here is my code:

a = [1, 2, 3, 4, 5]
for slice_index in range(0, len(a)):
    print (a[slice_index::] + a[:slice_index:])

[1, 2, 3, 4, 5]
[2, 3, 4, 5, 1]
[3, 4, 5, 1, 2]
[4, 5, 1, 2, 3]
[5, 1, 2, 3, 4]

Explanation: a[slice_index::] gives the part of array where index >= slice_index. Same for the other.

Gareth Ma
  • 309
  • 2
  • 11
1
a = [0,1,2,3,4,5,6,7,8]
b = []
for i in range(len(a)):
    b.append([])
    for j in range(len(a)):
        b[i].append(a[(i+j)%len(a)])

print b
Dawit Abate
  • 442
  • 4
  • 10
1

more_itertools.circular_shifts implements circular shifts, a type of cyclic permutation.

Code

import more_itertools as mit


iterable = range(9)

# Option 1
mit.circular_shifts(iterable)

Output

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

Alternatives

Using sliding windows via the same third-party library:

import itertools as it


# Option 2
list(it.islice(mit.windowed(it.cycle(iterable), n=len(iterable)), len(iterable)))

# Option 3
list(mit.windowed(mit.ncycles(iterable, n=2), n=len(iterable)))[:-1]
pylang
  • 28,402
  • 9
  • 97
  • 94
0
>>> L = ['a', 'b', 'c', 'd', 'e']
>>> [[L[i-j] for i in range(len(L))] for j in range(len(L), 0, -1)]
[['a', 'b', 'c', 'd', 'e'],
 ['b', 'c', 'd', 'e', 'a'],
 ['c', 'd', 'e', 'a', 'b'],
 ['d', 'e', 'a', 'b', 'c'],
 ['e', 'a', 'b', 'c', 'd']]
John La Rooy
  • 263,347
  • 47
  • 334
  • 476