Lists
Lists are an ordered list of elements of possibly different types identified by a number index. Each element in a list can be accessed individually by their index. Lists are constructed as a comma separated list of elements, can contain any type of value, and are enclosed by square brackets:
[
"Ghost",
57.3,
function (x) {
return x * x
}
]
Accessing Elements
You can access any element in a list by calling the subscript operator on it with the index of the element you want. Like most languages, indices start at zero:
vocabulary = ["activation", "propogate", "execute", "initialize"]
console.log(vocabulary[0]) // >> activation
console.log(vocabulary[1]) // >> propogate
console.log(vocabulary[2]) // >> execute
console.log(vocabulary[3]) // >> initialize
Iterating
for ... in walks a list, optionally handing you the index alongside each element:
for (word in vocabulary) {
console.log(word)
}
for (index, word in vocabulary) {
console.log(index.toString() + ": " + word)
}
An index that is out of range reads as null rather than raising an error, so guard with length() when the index is computed.
Arithmetic
Arithmetic operators work on lists of numbers, elementwise. A number spreads across every element, and two lists are applied to each other:
console.log([1, 2, 3] * 2) // >> [2, 4, 6]
console.log([1, 2, 3] + 10) // >> [11, 12, 13]
console.log([1, 2, 3] + [10, 20, 30]) // >> [11, 22, 33]
Shapes are lined up from the right, so a shorter shape stretches across a longer one rather than having to match it exactly. A row applies down every row of a matrix:
console.log([[1, 2], [3, 4]] + [10, 20]) // >> [[11, 22], [13, 24]]
console.log([[1, 2], [3, 4]] * 2) // >> [[2, 4], [6, 8]]
These are the same rules and the same operation as the math module's methods — a + b and math.add(a, b) are two ways of writing one thing. Lists have to be rectangular and hold only numbers to take part; anything else is an error rather than a guess.
+ does not join two lists. Joining is concat(), because the operators are arithmetic — if + meant joining, -, *, / and % would have no matching reading and the operators would stop agreeing with each other.
Comparing
== and != compare lists by their contents, to any depth, so a list you built compares equal to one you wrote out:
console.log([1, 2] == [1, 2]) // >> true
console.log([1, 2] + [1, 2] == [2, 4]) // >> true
console.log([[1], [2]] == [[1], [2]]) // >> true
Ordering two lists with < or > is not supported.
Methods
For the remainder of this documentation, we'll discuss each method available on lists.
Most of these return a new list and leave the receiver alone. The ones that mutate in place — push, pop, shift, unshift, insertAt, removeAt, and assignment through [] — say so below.
chunk()
The chunk method splits the list into a new list of smaller lists of at most size elements each. The last chunk holds whatever is left over:
[1, 2, 3, 4, 5].chunk(2)
// [[1, 2], [3, 4], [5]]
A size of zero or less is a value error.
every()
The every method reports whether every element satisfies a test. It stops at the first element that fails:
[1, 2, 3].every(function (value, index) {
return value > 0
})
// true
fill()
The fill method returns a new list with value in place of the elements from start up to but not including end. Both default to covering the whole list:
[1, 2, 3, 4].fill(9);
// [9, 9, 9, 9]
[1, 2, 3, 4].fill(0, 1, 3)
// [1, 0, 0, 4]
Like slice(), it names a range, so bounds outside the list are an index error. It does not mutate.
find()
The find method returns the first element matching a test, or null when nothing does:
[1, 2, 3].find(function (value, index) {
return value > 1
})
// 2
Pair it with indexOf(), which searches for a value rather than by a test.
findIndex()
The findIndex method is find() answering with the position instead of the element, or -1 when nothing matches:
[1, 2, 3].findIndex(function (value, index) {
return value > 1
})
// 1
flatMap()
The flatMap method maps over the list and splices any list a call returns into the result, one level deep. It is map() followed by a single level of flattening:
[1, 2].flatMap(function (value, index) {
return [value, value * 10]
})
// [1, 10, 2, 20]
flatten()
The flatten method returns a new list with every nested list's elements spliced in, all the way down:
[[1, [2]], [3]].flatten()
// [1, 2, 3]
There is no depth argument — it flattens completely.
indexOf()
The indexOf method returns the position of the first element equal to a value, or -1 if there is none. It compares the same way contains() does, so a nested list matches on its contents:
[1, 2, 3].indexOf(2); // 1
[1, 2, 3].indexOf(9); // -1
insertAt()
The insertAt method inserts a value at a position and returns the list's new length. It mutates the list:
values = [1, 2, 3]
values.insertAt(1, 99)
// values is now [1, 99, 2, 3]
An out-of-range position clamps to the nearest end rather than erroring.
isEmpty()
The isEmpty method reports whether the list has no elements — the same as length() == 0, and worth preferring over if (list), since an empty list is truthy:
[].isEmpty() // true
removeAt()
The removeAt method removes the element at a position and returns it. It mutates the list:
values = [1, 2, 3]
values.removeAt(1)
// 2, and values is now [1, 3]
An out-of-range position answers null, the same leniency pop() and shift() give an empty list.
splice. insertAt and removeAt cover the same ground as two single-purpose methods, in keeping with push/pop/shift.some()
The some method reports whether any element satisfies a test. It stops at the first that does:
[1, 2, 3].some(function (value, index) {
return value > 2
})
// true
unshift()
The unshift method adds a value to the front of the list and returns its new length — the counterpart to push(). It mutates the list:
values = [1, 2, 3]
values.unshift(0)
// 4, and values is now [0, 1, 2, 3]
concat()
The concat method joins two lists end to end, returning a new list and leaving both alone:
[1, 2].concat([3, 4])
// [1, 2, 3, 4]
Unlike the arithmetic operators, concat works on lists of anything, not just numbers. Use push() to add a single element instead of a whole list.
contains()
The contains method reports whether a value is somewhere in the list. Values are compared the same way == compares them, so lists match by contents rather than by identity:
[1, 2, 3].contains(2);
// true
[[1], [2]].contains([2])
// true
each()
The each method calls a function once per element, for its side effects, and returns the list itself so the call can be chained. The function receives the element and its index:
["a", "b"].each(function (value, index) {
console.log(index, value)
})
// 0 a
// 1 b
If the function returns an error, each stops there and hands the error back.
filter()
The filter method builds a new list of the elements a function accepts. The function receives the element and its index, and anything truthy it returns keeps the element:
[1, 2, 3, 4].filter(function (value, index) {
return value > 2
})
// [3, 4]
first()
The first method returns the first element in the list. If the list is empty, it returns null:
[1, 2, 3, 4].first()
// 1
join()
The join method joins the items in a list into a string. It takes a single argument, the string to use as the "glue" between the items in the list.
[1, 2, 3, 4].join('-')
// 1-2-3-4
last()
The last method returns the last element in the list. If the list is empty, it returns null:
[1, 2, 3, 4].last()
// 4
length()
The length method returns how many elements the list holds:
[1, 2, 3, 4].length()
// 4
map()
The map method builds a new list by running every element through a function. The function receives the element and its index:
[1, 2, 3].map(function (value, index) {
return value * 2
})
// [2, 4, 6]
For arithmetic specifically, the operators already broadcast — [1, 2, 3] * 2 is the same list without the function.
pop()
The pop method removes the last element from the list and returns it. It mutates the list. On an empty list it returns null:
list = [1, 2, 3, 4]
console.log(list.pop()) // >> 4
console.log(list) // >> [1, 2, 3]
Use shift() to take from the front instead.
push()
The push method adds an element to the end of the list and returns the list's new length. It mutates the list:
list = [1, 2, 3, 4]
console.log(list.push(5)) // >> 5
console.log(list) // >> [1, 2, 3, 4, 5]
reduce()
The reduce method folds the list down to a single value. The function receives the accumulator, the element, and the index:
[1, 2, 3].reduce(function (total, value, index) {
return total + value
})
// 6
An optional second argument seeds the accumulator. Without one, the first element seeds it — and reducing an empty list with no seed is an argument error, since there is nothing to answer with:
[1, 2, 3].reduce(function (total, value, index) {
return total + value
}, 10)
// 16
reverse()
The reverse method returns a new list with the elements in the opposite order:
[1, 2, 3].reverse()
// [3, 2, 1]
shift()
The shift method removes the first element from the list and returns it. It mutates the list. On an empty list it returns null:
list = [1, 2, 3]
console.log(list.shift()) // >> 1
console.log(list) // >> [2, 3]
slice()
The slice method returns a new list holding the elements from start up to but not including end. end defaults to the length of the list:
[1, 2, 3, 4].slice(1);
// [2, 3, 4]
[1, 2, 3, 4].slice(1, 3)
// [2, 3]
[] indexing, which answers null for an index outside the list, slice() raises an index error for bounds outside the list. A read that names a position is lenient; an operation that names a range validates it.sort()
The sort method returns a new list in order, leaving the original alone. The sort is stable.
With no argument, it sorts a list that is entirely numbers or entirely strings into natural order:
[3, 1, 2].sort()
// [1, 2, 3]
Anything else needs a comparator: a function of two elements returning a negative number, zero, or a positive number. Calling sort() with no comparator on a mixed list is an argument error explaining why.
[3, 1, 2].sort(function (a, b) {
return b - a
})
// [3, 2, 1]
tail()
The tail method returns a new list containing all but the first element of the list, leaving the original untouched. On an empty list it returns null.
[1, 2, 3, 4].tail()
// [2, 3, 4]
toString()
The toString method returns a string representation of the list:
[1, 2, 3, 4].toString()
// [1, 2, 3, 4]
unique()
The unique method returns a new list with repeats dropped, keeping the order each value was first seen in. Values are compared the same way contains() compares them:
[3, 1, 2, 1].unique()
// [3, 1, 2]
indexOf. Removing an element means filtering a new list without it.