LambdaLabTM
Computer Science · Class 11 · Lists Revisited
ProgramsCounting⏱️ 16 min read

Counting & Totals

Six programs that answer a question with a number. All of them are a collector above the loop and a test inside it — until the last one, which looks the same, is set constantly, and is wrong in almost every version you will be shown. The lists here are written into the program to keep the working half in view; putting a reading loop in front of any of them is Programs on a List You Read, in the submenu above.

1Program 1 — above and below the average

📋 The problem

Work out the average of a list of marks, then count how many are above it and how many below.

above_below.py
# how many items are above the average, and how many below?

marks = [72, 65, 88, 91, 54, 40]
total = 0

for m in marks:
    total = total + m

average = total / len(marks)
above = 0
below = 0

for m in marks:
    if m > average:
        above = above + 1
    elif m < average:
        below = below + 1

print('Average:', average)
print('Above:', above)
print('Below:', below)
Output
Average: 68.33333333333333
Above: 3
Below: 3
Tip
That average is not a bug. 410 ÷ 6 really does go on for ever, and Python prints as much of it as a float can hold. round(average, 2) gives 68.33 when you want something readable — but do the rounding when you print, never before the comparisons, or marks close to the average land on the wrong side.

An elif rather than a second if, and no else: a mark exactly equal to the average is neither above nor below, so it is counted in neither. That is why 3 + 3 happens to be 6 here and would not be if a mark landed exactly on it.

2Program 2 — how many times does one value appear?

📋 The problem

Count how often a given value occurs in a list, without count().

tally.py
# count how many times one value appears, without count()

numbers = [4, 7, 4, 2, 4, 9]
wanted = 4
count = 0

for n in numbers:
    if n == wanted:
        count = count + 1

print(wanted, 'appears', count, 'times')
Output
4 appears 3 times

The shortest program on the page, and the one whose shape everything else is built from. numbers.count(4) does the same job in one call — write the loop when the question asks for it, and when the test is something no method knows about, like “how many are even and above the average”.

3Program 3 — the evens and the odds, kept apart

📋 The problem

Add up the even numbers and the odd numbers separately.

even_odd_totals.py
# the totals of the even and the odd numbers, kept apart

numbers = [12, 7, 30, 45, 8, 21]
even_total = 0
odd_total = 0

for n in numbers:
    if n % 2 == 0:
        even_total = even_total + n
    else:
        odd_total = odd_total + n

print('Even numbers add up to', even_total)
print('Odd numbers add up to', odd_total)
Output
Even numbers add up to 50
Odd numbers add up to 73
Watch Out
These are totals, so they go up by n. Change both to + 1 and the program answers a different question — how many, not how much — and the answers (3 and 3) look every bit as plausible. The only way to tell them apart is to read what the variable is called and check the line agrees with it.

4Program 4 — how many in each grade band?

📋 The problem

Sort a list of marks into A (90+), B (75+), C (33+) and Failed.

grade_bands.py
# how many marks fall in each grade band?

marks = [92, 78, 45, 88, 30, 61, 75]
a_grade = 0
b_grade = 0
c_grade = 0
failed = 0

for m in marks:
    if m >= 90:
        a_grade = a_grade + 1
    elif m >= 75:
        b_grade = b_grade + 1
    elif m >= 33:
        c_grade = c_grade + 1
    else:
        failed = failed + 1

print('A:', a_grade)
print('B:', b_grade)
print('C:', c_grade)
print('Failed:', failed)
Output
A: 1
B: 3
C: 2
Failed: 1
Key Takeaway
The ladder goes strictest first, and the rungs lean on each other. By the time m >= 75 is asked, the mark is already known to be under 90 — so there is no need to write m >= 75 and m < 90. Put the loosest rung first and every mark becomes a C: 45, 88 and 92 all pass m >= 33.

Count the answers: 1 + 3 + 2 + 1 = 7, which is len(marks). Every mark landed in exactly one band, which is what the else guarantees.

grade_bands.py

5Program 5 — the second largest, and why it is hard

📋 The problem

Find the second largest number in a list.

Here is the version that gets written first, everywhere. It keeps two champions and updates them together:

second_broken.py
# the version everybody writes first

numbers = [20, 10]
largest = numbers[0]
second = numbers[0]

for n in numbers:
    if n > largest:
        second = largest
        largest = n
    elif n > second and n != largest:
        second = n

print('Largest:', largest)
print('Second largest:', second)
Output
Largest: 20
Second largest: 20
Watch Out
The second largest of [20, 10] is not 20. Both champions start at 20 — the first item — and 10 never beats either of them, so second is never touched. The program is right on every list whose largest value happens to arrive after something smaller, which is most of them, and that is exactly why the fault survives being tested.

The fix is to stop pretending second has a value before one has been found. Two passes, and a starting value that means “nothing different yet”:

second_largest.py
# the second largest number in a list

numbers = [45, 88, 12, 91, 67]
largest = numbers[0]

for n in numbers:
    if n > largest:
        largest = n

second = largest

for n in numbers:
    if n != largest:
        if second == largest or n > second:
            second = n

if second == largest:
    print('Every item in the list is the same')
else:
    print('Largest:', largest)
    print('Second largest:', second)
Output
Largest: 91
Second largest: 88
second = largest

Not a real answer — a marker meaning 'no different value has been found yet'. It is a value we know is in the list, and one that can be recognised later.

if second == largest or n > second:

Take this item if nothing has been taken yet, OR if it beats what has. The first half is what gets the search started without inventing a number.

if second == largest:

Still the marker, so nothing different was ever found: every item is the same. Saying so is better than printing a second largest that does not exist.

second_largest.py — on [20, 10], the list that broke the first version
Output
Largest: 20
Second largest: 10
second_largest.py — on [5, 5, 5]
Output
Every item in the list is the same
Key Takeaway
Test a list of two, and a list that is all the same. Those two cases break more “second largest” programs than anything else, and neither of them looks like an edge case until you try it. A program that is right on [45, 88, 12, 91, 67] has proved almost nothing.

6Program 6 — the same answer, the short way

second_sorted.py
# the second largest, using sorted()

numbers = [45, 88, 12, 91, 67]
in_order = sorted(numbers)

print('Sorted:', in_order)
print('Largest:', in_order[-1])
print('Second largest:', in_order[-2])
Output
Sorted: [12, 45, 67, 88, 91]
Largest: 91
Second largest: 88

sorted() hands back a new sorted list and leaves the original alone, so numbers is still in its original order afterwards. in_order[-1] is the last item and in_order[-2] the one before it.

Watch Out
It answers a slightly different question. On [91, 91, 45] this prints 91 as the second largest, because the second largest item really is another 91 — while the loop version answers 45, the second largest value. Neither is wrong; read the question and know which one you have written.
Tip
There is a third way, and it is the shortest. Find the largest with max(), take it out with remove(), and ask for the largest again — three lines, no champions to initialise. It is on The Built-in Way at the end of this submenu, along with the copy you have to make first.

7Recap

A counter goes up by 1, a total by the item

The variable's name should tell you which. Getting it wrong gives a plausible number and no error at all.

Ladders go strictest first

The rungs lean on each other, so no rung needs an upper limit written into it. Loosest first and everything lands on the first rung.

Round when you print, not before

round() in a comparison moves marks across the boundary. Keep the full value for the maths and shorten it for the reader.

Second largest needs a marker, not a guess

second = largest means 'nothing different found yet'. Starting it at the first item quietly answers 20 for [20, 10].

✍️ Now write these yourself
  1. 1

    Count how many numbers in a list are both even and above 50.

    Hint · One if with an and — no method answers this, which is the point.

  2. 2

    Find the smallest value, and how many times it appears.

    Hint · Two passes: the champion first, then a tally of items equal to it.

  3. 3

    Count how many marks are within 5 of the average, either side.

    Hint · The two-pass shape, with m >= average - 5 and m <= average + 5.

  4. 4

    Find the second smallest value in a list.

    Hint · Program 5 turned round. Test it on a two-item list before you believe it.

  5. 5

    Count the items in a list of names that begin with a vowel.

    Hint · name[0].lower() in 'aeiou', and mind an empty name.

Quick Check

A grade ladder is written with if m >= 33 first, then elif m >= 75, then elif m >= 90. What grade does 92 get?

Quick Check

Why does second = numbers[0] fail on [20, 10]?

Quick Check

sorted(numbers) is used to find the second largest. What happens to numbers?