3

There are multiple ways to loop back in python For example we have

arr=[5,6,8]
for i in range(len(arr)-1, -1, -1):
  print(i," ",arr[i])

which gives

2   8
1   6
0   5

I can solve what I need with that, but I am curious. There is another way to loop back which is

for im in arr[::-1]:
  print(im) 

Looks nice, right? This gives

8
6
5

My question is, using this second method is there a way to get not only the element but also the index? (the 2,1,0)

KansaiRobot
  • 7,564
  • 11
  • 71
  • 150
  • Does this answer your question? [Accessing the index in 'for' loops?](https://stackoverflow.com/questions/522563/accessing-the-index-in-for-loops) – mkrieger1 Sep 08 '21 at 11:28
  • Think about `enumerate` to get (index and num) as a tuple pair – Daniel Hao Sep 08 '21 at 11:29
  • 1
    If you need _only_ the index, analogous to `range(len(arr)-1, -1, -1)`, you can also use `reversed(range(len(arr)))`, which might be more intuitive and readable. – tobias_k Sep 08 '21 at 11:32

7 Answers7

3

use the builtin method reversed

arr = [5, 6, 8]
for i in reversed(range(0,len(arr))):
    print(i,arr[i])
theunknownSAI
  • 300
  • 1
  • 4
  • While it's not very pythonic to use list indices in Python, this question might be a case of "keep it simple, s". I like this solution for its simplicity. – joanis Sep 08 '21 at 14:37
2

There's a builtin for that: reversed. This might be more readable and intuitive than using a range or slice with negative step.

For just the index, you can pass the range to reversed directly:

arr = list("ABC")

for i in reversed(range(len(arr))):
    print(i, arr[i])                   
# 2 C
# 1 B
# 0 A

For index and element, you have to collect the enumerate iterator into a list before reversing:

for i, x in reversed(list(enumerate(arr))):
    print(i, x)                
# 2 C
# 1 B
# 0 A
tobias_k
  • 81,265
  • 12
  • 120
  • 179
  • wait, I don't get why the need of list, if the first code worked – KansaiRobot Sep 08 '21 at 11:49
  • @KansaiRobot `enumerate` returns a generic iterator/generator that can not be reversed directly. `range` is a special range-iterator that can be reversed directly because it's length is known a priori. In the case of `enumerate(arr)` you in fact also know the length, but you could also create an `enumerate` from e.g. a `zip`, `filter`, or any other kind of generator/iterator, and then it would not be possible, so `enumerate` in general can not be reversed, either, since you do not know the length or, in fact, the last element, before generating all the others.. – tobias_k Sep 08 '21 at 12:42
2

Here's a way using reversed() and zip() that should be memory efficient:


arr = [5, 6, 8]

for idx, elem in zip(reversed(range(len(arr))),
                     reversed(arr)
                     ):
    print(idx, elem)

Output:

2 8
1 6
0 5
Terry Spotts
  • 3,527
  • 1
  • 8
  • 21
1
for idx, elem in list(enumerate(arr))[::-1]:
    print(idx, elem)

This uses enumerate to keep elements with their specific index. Then we convert it to list and reverse (enumerate result is not reversible without converting). No need to keep track of any order (like subtracting) manually

You can also just reverse the forward range:

for idx in range(len(arr))[::-1]:
   print(idx, arr[idx])

Python actually reverses range in a smart way, without converting it to a list, so it works even for big stuff:

>>> range(10)[::-1]
range(9, -1, -1)

This means that range(len(arr))[::-1] will work in exactly the same way, with exactly the same speed, as your range(len(arr)-1, -1, -1) while looking nice!

h4z3
  • 5,265
  • 1
  • 15
  • 29
  • This forces however to consume the generator – mozway Sep 08 '21 at 11:32
  • @mozway its impossible to reverse-enumerate a sequence without knowing its length beforehand. – Kaihaku Sep 08 '21 at 11:39
  • @mozway Ye, you cannot reverse a generator in any other way - but it's still less error-prone than manually counting indexes and works well for smaller stuff. In the meantime, I added another solution that's basically the same what OP posted originally with range but looks nicer. – h4z3 Sep 08 '21 at 11:40
  • @h4z3 this is a nicer approach ;) – mozway Sep 08 '21 at 11:41
  • Creating an entirely new list in memory just to iterate over is awful. – salt-die Sep 08 '21 at 11:46
  • @salt-die For 10 minutes before you posted your comment, there's been a completely generator approach added to my answer which I openly say works the same as OP's original approach while looking nicer. – h4z3 Sep 08 '21 at 11:51
  • There's literally another user that posted exactly the same two approaches as me, later than me, just in different order than me. Still before your comment. But it's me who gets shitted on in comments and gets a downvote. – h4z3 Sep 08 '21 at 11:53
0
i = len(arr) - 1
for im in arr[::-1]:
    print(i, " ", im)
    i -= 1
thebadgateway
  • 433
  • 1
  • 4
  • 7
-1

You can use enumerate and subtract from the initial length:

arr=[5,6,8]
for i,im in enumerate(arr[::-1]):
    print(len(arr)-i-1, im)

output:

2 8
1 6
0 5
mozway
  • 194,879
  • 13
  • 39
  • 75
-1

The normal way to iterate with an index is with enumerate. One can use this to index the other way by subtracting the index from the length of the sequence:

In [51]: arr = [5, 6, 8]
    ...: for i, n in enumerate(arr):
    ...:     print(i, n)
    ...: 
0 5
1 6
2 8

In [52]: for i, n in enumerate(reversed(arr)):
    ...:     print(len(arr) - i - 1, n)
    ...: 
2 8
1 6
0 5

Note that reversed doesn't create a new list in memory and is preferable to [::-1] if just iterating.

salt-die
  • 806
  • 6
  • 7