Skip to content

P2.2 — Lists & Collections

What you'll learn

  • What an array is: an ordered list of values held under one name.
  • That positions are counted from 0, and how to read one with array[index].
  • How to go through every item with a for loop, add items, and ask how many there are.
  • What a dictionary is: a collection that looks values up by a name (key) instead of a position.

How it applies

  • One name for many values. Tracking 50 enemies as 50 separate variables is hopeless; an array holds them all under one name, so the program can process "all of them" with a single loop.
  • Counting from zero is the rule, and the trap. The first item is at index 0, so the last item of a 3-item list is at index 2, not 3. Reaching for the position "after the end" is the most common list error.
  • Loops and lists are partners. A for loop over a list runs its body once per item — exactly how you apply the same step to every element (alert every enemy, total every price).
  • Dictionaries name their data. When values are not a sequence but labelled facts — "hp", "armor", "name" — a dictionary looks them up by key, which reads far better than remembering which position held what.

Concepts

Arrays: an ordered list

An array is an ordered list of values stored under one name, written in square brackets:

var items := ["sword", "shield", "potion"]

items holds three values in order. You read one by its position, called its index, in square brackets — and positions start at 0:

print(items[0])     # sword   — the FIRST item is index 0
print(items[1])     # shield
print(items[2])     # potion  — the third (and last) item is index 2

Counting from zero is the single most important thing here. In a three-item list, the valid indexes are 0, 1, 2. There is no index 3 — asking for items[3] is reaching past the end, an error. The last valid index is always one less than the number of items.

How many, and adding more

size() tells you how many items the array holds; append(...) adds one to the end:

print(items.size())     # 3
items.append("torch")   # now ["sword", "shield", "potion", "torch"]
print(items.size())     # 4

Because indexes start at 0, a list of size() 4 has valid indexes 0 through 3 — the largest index is size() - 1. Keeping that relationship straight is what avoids off-by-one mistakes when you index a list.

Going through every item with for

The natural partner of a list is a loop. A for loop can walk a list directly, giving you each item in turn:

for item in items:
    print(item)         # prints each value: sword, shield, potion, torch

Here item takes the value of each element, one per pass — no index needed. This is how you apply the same action to every element: total a list of prices, alert every enemy, draw every tile. (You can still loop by index with for i in range(items.size()) when you need the position itself.)

Dictionaries: look up by name

Some collections are not an ordered sequence but a set of labelled values. A dictionary maps a key (a name) to a value, written in curly braces:

var stats := {"hp": 30, "armor": 5}
print(stats["hp"])         # 30   — look up by key, not position
stats["mana"] = 10         # add or change a key
print(stats["armor"])      # 5

You reach a value by its key (stats["hp"]) rather than a numeric position. Use an array when order and "all of them" matter (a list of items to loop over); use a dictionary when each value has a meaningful name (the hp, the armor, the level).

Example

Lists plus loops, doing real work — totalling prices:

var prices := [10, 25, 5, 50]
var total := 0
for price in prices:        # price is 10, then 25, then 5, then 50
    total = total + price   # runs once per item, accumulating
print(total)                # 90

The loop visits each of the four prices and adds it to total, which ends at 90. Notice you never wrote an indexfor price in prices hands you the values directly. The same four-line shape sums any list of any length, which is the power of pairing a list with a loop: write the per-item step once, and it applies to every item, however many there are.

Walkthrough

Use your P1.1 script setup.

  1. Make var items := ["sword", "shield", "potion"]. Print items[0] and items[2]. Then print items[3] and read the error — there is no index 3 in a three-item list.
  2. Print items.size() (3). append a fourth item, print size() again (4), and now print items[3] successfully. Note how the largest valid index grew to size() - 1.
  3. Loop over the list with for item in items: print(item) and confirm every item prints in order.
  4. Make var prices := [10, 25, 5, 50] and total them with a loop (as in the example); confirm 90. Then make a dictionary var stats := {"hp": 30, "armor": 5}, print stats["hp"], add a "mana" key, and print it.

Optional sanity check

With var a := [10, 20, 30], predict what a[a.size()] does before running. a.size() is 3, and there is no index 3 (valid are 0,1,2), so it errors — reaching one past the end. The last item is a[a.size() - 1], i.e. a[2]. That size() versus size() - 1 gap is the list off-by-one.

Self-check quiz

Q1 — In var x := [\"a\", \"b\", \"c\"], what is x[0], and what is the last valid index?

A. x[0] is \"b\"; the last index is 3. B. x[0] is \"a\"; the last valid index is 2 (positions start at 0). C. x[0] is the size; the last index is 3. D. x[0] errors; indexing starts at 1.

Reveal answer

B. Indexes start at 0, so x[0] is the first item "a", and a three-item list has valid indexes 0, 1, 2 — the last is 2, one less than the size. A starts at the wrong position. C and D assume 1-based indexing, which this is not.

Q2 — How do you perform the same action on every item of a list things?

A. Write the action once for each item by hand. B. for item in things: with the action in the loop body, so it runs once per item. C. things.size() performs the action. D. You cannot; lists are read-only.

Reveal answer

B. A for loop over the list runs its body once per element, applying the same step to each — the list-and-loop partnership. A defeats the purpose (and fails for unknown lengths). C only counts items. D is false — lists are not read-only.

Q3 — When is a dictionary a better fit than an array?

A. Always; arrays are obsolete. B. When each value has a meaningful name/key (hp, armor) and you look it up by that name rather than by position. C. Only for numbers. D. When you need the values in order.

Reveal answer

B. A dictionary maps keys to values, ideal when data is labelled ("hp", "armor") and accessed by name. A is false. C is wrong (keys and values can be many types). D describes when an array (ordered) is the better fit.

Integration question

Q4 — open

You have a list of enemy health values healths := [10, 5, 20, 8] and want to (a) print each enemy's health, and (b) count how many enemies have more than 9 health. Sketch the code combining a loop, indexing/iteration, and a decision (P1.5), and explain why you would iterate with for h in healths rather than for i in range(healths.size()) here — then name the off-by-one mistake that range version would invite.

Reveal expected answer

Iterate the list and use a counter with an if:

var healthy := 0
for h in healths:
    print(h)              # (a) print each value
    if h > 9:             # (b) decision per item
        healthy = healthy + 1
print(healthy)            # 2  (10 and 20 exceed 9)

The loop body runs once per enemy (four times), printing each health and, when the value exceeds 9, incrementing healthy; the if is inside the loop so the decision is made per item, and healthy (declared outside) survives across iterations to hold the running count, ending at 2. Why for h in healths: it hands you each value directly, with no index arithmetic, so there is nothing to get wrong — cleaner and safer when you only need the values. The range off-by-one it avoids: for i in range(healths.size()) is correct only with range(healths.size()) giving 0..size()-1; the tempting mistakes are range(healths.size() + 1) (runs one pass too many and indexes one past the end → error) or starting/stopping at the wrong number. Since the last valid index is size() - 1, the index-based loop is exactly where the list off-by-one bites, and iterating by value sidesteps it.