150

In my example code below, is the counter = 0 really required, or is there a better, more Python, way to get access to a loop counter? I saw a few PEPs related to loop counters, but they were either deferred or rejected (PEP 212 and PEP 281).

This is a simplified example of my problem. In my real application this is done with graphics and the whole menu has to be repainted each frame. But this demonstrates it in a simple text way that is easy to reproduce.

Maybe I should also add that I'm using Python 2.5, although I'm still interested if there is a way specific to 2.6 or higher.

# Draw all the options, but highlight the selected index
def draw_menu(options, selected_index):
    counter = 0
    for option in options:
        if counter == selected_index:
            print " [*] %s" % option
        else:
            print " [ ] %s" % option
        counter += 1


options = ['Option 0', 'Option 1', 'Option 2', 'Option 3']

draw_menu(option, 2) # Draw menu with "Option2" selected

When run, it outputs:

 [ ] Option 0
 [ ] Option 1
 [*] Option 2
 [ ] Option 3
Andre Miller
  • 14,161
  • 6
  • 49
  • 52
  • can you simply use the array length property instead of the for in loop method, for i < array.length? – Jim Jul 26 '09 at 21:08

4 Answers4

259

Use enumerate() like so:

def draw_menu(options, selected_index):
    for counter, option in enumerate(options):
        if counter == selected_index:
            print " [*] %s" % option
        else:
            print " [ ] %s" % option    

options = ['Option 0', 'Option 1', 'Option 2', 'Option 3']
draw_menu(options, 2)

Note: You can optionally put parenthesis around counter, option, like (counter, option), if you want, but they're extraneous and not normally included.

ArtOfWarfare
  • 17,763
  • 14
  • 122
  • 177
Evan Fosmark
  • 89,147
  • 33
  • 99
  • 116
6

I'll sometimes do this:

def draw_menu(options, selected_index):
    for i in range(len(options)):
        if i == selected_index:
            print " [*] %s" % options[i]
        else:
            print " [ ] %s" % options[i]

Though I tend to avoid this if it means I'll be saying options[i] more than a couple of times.

Laurence Gonsalves
  • 125,464
  • 31
  • 220
  • 273
  • 5
    In such a case you should always use enumerate() – Georg Schölly Jul 26 '09 at 21:35
  • gs, what if you only need to get the element every once in a while, but you need the index every time? It seems like in those situations this could be beneficial because you aren't creating a new variable each time. – Evan Fosmark Jul 26 '09 at 21:43
  • 1
    @gs: yes. That answer was already posted though, so I was posting an alternate solution. As usual, which answer is best depends on the details of your situation. – Laurence Gonsalves Jul 26 '09 at 21:58
  • 1
    @EvanFosmark - There's absolutely no performance difference between the two. I checked both with the following: `python -m timeit "s = 'String used for testing'; ''.join(str(i)+str(c) for i, c in enumerate(s))"` and `python -m timeit "s = 'String used for testing'; ''.join(str(i)+str(s[i]) for i in xrange(len(s)))"`. Each of them averaged on 8.83 usec per loop over 100,000 loops. Other tests (too long for a comment) showed very slight (~2%) speed increases for `xrange(len(s))` if it only needed to access the element on every 16 iterations. – ArtOfWarfare Oct 15 '14 at 18:48
4

You could also do:

 for option in options:
      if option == options[selected_index]:
           #print
      else:
           #print

Although you'd run into issues if there are duplicate options.

thedz
  • 5,284
  • 2
  • 22
  • 28
3

enumerate is what you are looking for.

You might also be interested in unpacking:

# The pattern
x, y, z = [1, 2, 3]

# also works in loops:
l = [(28, 'M'), (4, 'a'), (1990, 'r')]
for x, y in l:
    print(x)  # prints the numbers 28, 4, 1990

# and also
for index, (x, y) in enumerate(l):
    print(x)  # prints the numbers 28, 4, 1990

Also, there is itertools.count() so you could do something like

import itertools

for index, el in zip(itertools.count(), [28, 4, 1990]):
    print(el)  # prints the numbers 28, 4, 1990
Martin Thoma
  • 91,837
  • 114
  • 489
  • 768