#13

Counting

  • functions
  • challenge

Counting

If at first you do not succeed, call it version 1.0.

Hey pips!

When using a for loop, it’s often really useful to know which iteration number we’re on.

>>> for i in range(3):
        print(f"iteration {i}")
iteration 0
iteration 1
iteration 2

But if we’re iterating over an iterable, we don’t have a variable to count the iteration…

>>> l = ["clear", "wing"]

>>> for item in l:
        print(f"unknown iteration: {item}")
unknown iteration: clear
unknown iteration: wing

A pretty quick fix is to just make your own counting variable, and manually increment it each iteration.

>>> i = 0
>>> l = ["starving", "venom"]

>>> for item in l:
        print(f"iteration {i}: {item}")
        i += 1  # manually increment
iteration 0: starving
iteration 1: venom

[!Tip] This approach is useful when you need to do other stuff with the increment variable – although to be honest, if you’ve reached that stage it may be indicative of other issues.

But with how often this comes up, you would think there’s an in-built solution.

Well this is Python, so of course there is!

>>> l = ["phantom", "knight"]

>>> for i, item in enumerate(l):
        print(f"iteration {i}: {item}")
iteration 0: phantom
iteration 1: knight

The built-in enumerate() function works on any iterable. It pairs each item with its index to form an (index, item) tuple, so that when you iterate over it you can extract both the iteration index and item value.

>>> l = ["blue", "eyes", "white", "dragon"]

>>> list(enumerate(l))
[(0, "blue"),
 (1, "eyes"),
 (2, "white"),
 (3, "dragon")]

You can use enumerate() on any iterable, including str, tuple and even dict objects.

>>> list(enumerate("sup"))
[(0, "s"),
 (1, "u"),
 (2, "p")]

Using this, we now have an extremely convenient way to count iterations while we’re looping – particularly in list comprehensions:

>>> text = "Never Gonna Give You Up"
>>> out = [
        # capitalise every other character
        char.upper() if i % 2 == 0 else char.lower()
        for i, char in enumerate(text)
    ]
>>> "".join(out)
NeVeR GoNnA GiVe yOu uP

And fun fact, you can even pass in a second numerical argument to specify the starting index!

>>> l = ["iTechnicals", "Sup"]
>>> [f"{i}: {player}" for i, player in enumerate(l, 1)]
["1: iTechnicals", "2: Sup"]

Keep in mind enumerate() doesn’t exactly return a list, so you can’t index it:

>>> e = enumerate("phantasm")
>>> e[1]
Error:

If you want the raw items, just convert the output to a list with the list() constructor.

>>> l = list(enumerate("desync"))
>>> l[2]
(2, "s")

This is because enumerate() actually returns an iterator which acts as a proxy to the original object. We’ll take a closer look at these a future issue!


Challenge

Given a list of fruit and how many to purchase as tuples, can you print a shopping list?

>>> shopping = [
        ("apricots", 3),
        ("bloomerangs", 4),
        ("carrots", 2),
        ("dragonfruit", 1)
    ]

>>> (your_expression)
1. apricots x3
2. bloomerangs x4
3. carrots x2
4. dragonfruit

And bonus points for making it as fancy as you can :P

=====================
| 1 | durian  |  x7 |
| 2 | acai    |  x3 |
| 3 | lychee  | x10 |
| 4 | pomelo  |  x2 |
---------------------
|     TOTAL   | x22 |
=====================


Question? Bug needs fixing? Or just want to nerd out over programming?
Drop a message in the GitHub discussion for this issue.