13.4 Decisions and repetition
- Approved by
- Panaversity ·
In everyday life. A mail clerk sorts the morning mail. Each envelope stamped BILL goes in the AP tray. Every other envelope goes in the general tray, even a bill that nobody stamped. The clerk repeats one small decision for every envelope. That is most of what a program does too.
What you will learn. How to find each decision and each repeated step, and say it as a sentence.
Why it matters. Each decision is a rule, and an empty value can slip through one.
A condition is a yes-or-no question about a value, such as whether the status is open. An if runs some lines only when its condition is true, and an else runs others when it is false. A loop repeats lines once for each item in a collection. Two words join conditions. With and, both must be true. With or, either one is enough, and a or b gives b when a is empty.
| Shape | Reads as | Brightline use |
|---|---|---|
if ... else ... | Do this, or that | An open invoice is listed, a paid one is skipped |
| A loop over items | For each invoice, do this | Print one line per invoice |
| A filter | Keep only the items whose answer is yes | Keep the open invoices |
a or b | Use a, or use b if a is empty | Stated due date, or the one computed from the terms |
while | Repeat while a condition stays true | Fetch pages of invoices until one comes back empty |
How do I find each decision?
Look for every place where the code chooses: an if, a filter, or an or.
At work. Ana's Chapter 12 script does three things in a row for every invoice. It skips the invoice unless it is open. It takes the invoice's due date, or computes one from the terms. And it keeps the invoice if the due date is on or before Friday. Reading the script means finding those three decisions and checking each one against the rule. The agent's first plan in Chapter 12 missed the second one.
The plainest decision is an if. Here is the mail clerk's rule, in both languages. === in TypeScript and == in Python ask whether two values are equal. Predict what each prints.
const stamp = "BILL";
if (stamp === "BILL") {
console.log("AP tray");
} else {
console.log("general tray");
}stamp = "BILL"
if stamp == "BILL":
print("AP tray")
else:
print("general tray")Both print AP tray. With any other stamp, both print general tray.
A filter asks the same question of every item. In TypeScript, each invoice is written in braces, as names with their values. The filter keeps each invoice, inv, whose status is "open". Its rule is written in place with an arrow: (inv) => ... means "for each inv, the answer is what follows the arrow". || is TypeScript's or. Boolean(...) shows whether a value counts as true or false. Predict both lines of the loop, then the last line.
const invoices = [
{ number: "INV-8301", status: "open", due: "2027-03-03" },
{ number: "INV-8309", status: "paid", due: "2027-02-28" },
{ number: "INV-8305", status: "open", due: "" },
];
const open = invoices.filter((inv) => inv.status === "open");
for (const inv of open) {
const due = inv.due || "from the terms";
console.log(inv.number, due);
}
console.log(Boolean(""), Boolean([]));INV-8301 2027-03-03
INV-8305 from the terms
false trueIn Python. The filter here is a list comprehension: a list built from another list in one line, read as "each inv in invoices, if its status is open". The label list[dict[str, str]] says it is a list of records, each with text names and text values.
invoices: list[dict[str, str]] = [
{"number": "INV-8301", "status": "open", "due": "2027-03-03"},
{"number": "INV-8309", "status": "paid", "due": "2027-02-28"},
{"number": "INV-8305", "status": "open", "due": ""},
]
open_invoices = [inv for inv in invoices if inv["status"] == "open"]
for inv in open_invoices:
due = inv["due"] or "from the terms"
print(inv["number"], due)
print(bool(""), bool([]))INV-8301 2027-03-03
INV-8305 from the terms
False FalseWhy can an empty value slip through a decision?
Some values count as false, and not all of them are empty. In a condition, both languages treat some values as false even though they are not false: the number 0, empty text "", and "nothing". That is why due || computed works. An empty due date counts as false, so the computed one is used. It is also a trap. An amount of 0 counts as false too, so a condition meant to ask "is there an amount?" would treat a zero-dollar credit as missing.
How does a loop stop?
A for loop runs once for each item you already have. A while loop repeats as long as its condition stays true. It is for work where you do not know the count in advance, like a clerk opening mailbags until one comes empty, or reading pages of invoices until one comes back empty. Every while loop needs a way to stop. That is a line that changes something its condition checks, or a break or return that leaves the loop. Without one, the loop never ends. pages is a list of lists, one for each page, and pages[page] is the one at that position. let makes a variable that can change, and .length in TypeScript and len() in Python count a list's items. Predict the last line.
In TypeScript.
const pages = [["INV-8301", "INV-8302"], ["INV-8306"], []];
let page = 0;
while (pages[page].length > 0) {
console.log("page", page + 1, "has", pages[page].length);
page += 1;
}
console.log("stopped at page", page + 1);In Python.
pages = [["INV-8301", "INV-8302"], ["INV-8306"], []]
page = 0
while len(pages[page]) > 0:
print("page", page + 1, "has", len(pages[page]))
page += 1
print("stopped at page", page + 1)Both print:
page 1 has 2
page 2 has 1
stopped at page 3page += 1 adds 1 to page, and it is the line that moves the loop toward its end. Positions count from 0, so the code prints page + 1.

Figure 13.4. A while loop. Each pass checks the condition again, and page += 1 is what moves it toward the empty page that stops it.
Where the languages differ.
| TypeScript | Python | |
|---|---|---|
| Equal | === (always use this one, not ==) | == |
| And, or, not | &&, ` | |
| An empty list in a condition | Counts as true | Counts as false |
| Repeat while true | while (cond) { ... } | while cond: |
The row about an empty list causes real bugs. "If there are any errors, stop" written as if (errors) in TypeScript is always true, even when the list of errors is empty. Check the length instead: errors.length > 0.
The operating rule: find every decision in the code and say it as a sentence, then check each sentence against the business rule, including what happens to empty values. For every while loop, find the line that makes it stop.

Figure 13.5. A row through a filter. Each gate is one condition.
Practice it. Practice: decisions and loops, in your own tool, ten minutes.
Check yourself
Question 1 / 8
0 answered
The invoice loop uses inv.due || "from the terms". What does it print for INV-8305, whose due is empty?
- Owner
- Panaversity