Lists
A shopping list, the scores in a game, the names in a class: most things worth a program come in lists. In TJS a list is called an array: values in order, inside square brackets, separated by commas.
Making a list
const fruits = ['apple', 'banana', 'cherry']
preview.append(`The first fruit is ${fruits[0]}, and there are ${fruits.length} of them.`)
test 'reading a list' {
expect(fruits[0]).toBe('apple')
expect(fruits[2]).toBe('cherry')
expect(fruits.length).toBe(3)
}
fruits[0]is the first item. Counting starts at 0, so the third item isfruits[2]. That surprises everyone once; after that it is just how lists work.fruits.lengthis how many items there are.
Ask the console for fruits[1], then fruits[3]: there is no fourth fruit, so the answer is
undefined, meaning "nothing here".
Going through a list
for … of runs a block once for each item, with a name for the current one:
function total(prices: [0.0]) {
let sum = 0
for (const price of prices) {
sum = sum + price
}
return sum
}
preview.append(`The bill comes to ${total([2.5, 4, 1.25])}.`)
test 'adding up a list' {
expect(total([2.5, 4, 1.25])).toBe(7.75)
expect(total([])).toBe(0)
}
The example [0.0] says what prices is: a list of numbers. An empty list is still a list
of numbers, so total([]) is fine (and 0). A list with a word in it, or a single number that
is not in a list, is refused with an error that says what was expected.
Growing a list
push adds an item to the end. A loop and push together build a list:
function squares(n: 0) {
const result = []
for (let i = 1; i <= n; i++) {
result.push(i * i)
}
return result
}
preview.append(squares(6).join(', '))
test 'the first squares' {
expect(squares(4)).toEqual([1, 4, 9, 16])
expect(squares(0)).toEqual([])
}
join(', ') turns a list into one string, with ', ' between the items. In a test, compare
lists with toEqual, which looks at what is inside them.
Changing every item: map
map makes a new list by doing the same thing to every item:
const prices = [10, 20, 30]
const withTax = prices.map((price) => price * 1.5)
preview.append(withTax.join(', '))
test 'map does the same thing to every item' {
expect(withTax).toEqual([15, 30, 45])
expect(prices).toEqual([10, 20, 30])
}
(price) => price * 1.5 is a small function with no name: given a price, it gives back
price * 1.5. The => arrow is a short way to write a function you only need here. map
calls it once for each item and collects the answers. The original list is not changed: the
second check in the test says so.
Keeping some items: filter
filter makes a new list of only the items your function says yes to:
const scores = [42, 87, 65, 91, 38, 75]
const passing = scores.filter((score) => score >= 50)
preview.append(`${passing.length} passed: ${passing.join(', ')}`)
test 'filter keeps what passes the test' {
expect(passing).toEqual([87, 65, 91, 75])
}
Showing a list on the page
Put map and the building blocks from Hello, Screen together, and a list of data becomes a list on
the page:
import { elements } from 'tosijs'
const { ul, li } = elements
const planets = ['Mercury', 'Venus', 'Earth', 'Mars']
preview.append(ul(...planets.map((planet) => li(planet))))
ul makes a list and li makes one item in it. planets.map(…) turns each name into an item,
and the ... in front spreads the items out, so ul gets them one by one instead of as a
single list. Add a planet to the array and it appears on the page.
Try it
Write longest(words: ['']) that gives back the longest word in a list, and test it with a
few lists, including one with two words of the same length (decide which one should win, and
test that). Then show the words from a list that are longer than four letters, as a list on
the page.