Decisions
Every program so far has done the same thing every time. Real programs decide: if the user is new, say welcome; if the number is too big, say so. This chapter is about making a program choose what to do.
if: only sometimes
if runs a block of code only when a condition is true. The condition goes in parentheses,
and the code to run goes in braces:
function advice(temperature: 0) {
let message = 'Have a nice day.'
if (temperature < 10) {
message = 'Take a coat.'
}
return message
}
preview.append(advice(5))
test 'a coat when it is cold, not when it is warm' {
expect(advice(5)).toBe('Take a coat.')
expect(advice(20)).toBe('Have a nice day.')
}
temperature < 10 is a comparison, so its value is true or false (see Values and Names). When it is
true, the block runs and message changes; when it is false, the block is skipped. Change
advice(5) to advice(25) and watch the advice change.
else: one or the other
else gives the other choice: what to do when the condition is false.
function describe(n: 0) {
if (n % 2 == 0) {
return `${n} is even`
} else {
return `${n} is odd`
}
}
preview.append(describe(7))
test 'even and odd' {
expect(describe(4)).toBe('4 is even')
expect(describe(7)).toBe('7 is odd')
}
% gives the remainder after dividing: 7 % 2 is 1, because 7 is three 2s and 1 over.
An even number leaves no remainder. Notice that each branch can return straight away: once a
function returns, nothing after it runs.
else if: more than two choices
Chain else if to test one condition after another. The first one that is true wins, and the
rest are skipped:
function grade(score: 0) {
if (score >= 90) {
return 'A'
} else if (score >= 80) {
return 'B'
} else if (score >= 70) {
return 'C'
} else {
return 'keep practising'
}
}
preview.append(`85 earns ${grade(85)}`)
test 'grades' {
expect(grade(95)).toBe('A')
expect(grade(85)).toBe('B')
expect(grade(70)).toBe('C')
expect(grade(10)).toBe('keep practising')
}
The order matters. 95 is also at least 80, but the score >= 90 test comes first, so it wins.
Try moving the score >= 80 branch to the top and see which tests fail.
Combining conditions
Three words build bigger conditions out of smaller ones:
&&means and: both must be true.||means or: at least one must be true.!means not: it turns true into false and false into true.
function canRide(age: 0, height: 0) {
return age >= 8 && height >= 120
}
preview.append(`An 8-year-old who is 130cm tall can ride: ${canRide(8, 130)}`)
test 'old enough AND tall enough' {
expect(canRide(8, 130)).toBe(true)
expect(canRide(8, 100)).toBe(false)
expect(canRide(6, 130)).toBe(false)
}
given: choosing by value
When the decision is "which of these values is it?", a chain of else ifs gets long. given
lists the values instead. Each line names one or more values, then the block to run:
function kindOfDay(day: 'monday') {
given day {
'saturday', 'sunday' {
return 'weekend'
}
'friday' {
return 'almost the weekend'
}
} else {
return 'a working day'
}
}
preview.append(`Friday is ${kindOfDay('friday')}.`)
test 'days of the week' {
expect(kindOfDay('sunday')).toBe('weekend')
expect(kindOfDay('friday')).toBe('almost the weekend')
expect(kindOfDay('tuesday')).toBe('a working day')
}
Exactly one block runs: the one whose value matches, or the else block when none does.
(Other languages have switch for this. It is easy to get wrong, so TJS gives you given.)
Try it
A cinema charges children (under 12) 5, seniors (65 and over) 6, and everyone else 10. Write
ticketPrice(age: 0) and test all three prices, plus the edges: an 11-year-old, a
12-year-old, a 64-year-old and a 65-year-old. Edges are where decisions go wrong, so they are
where tests earn their keep.