Docs/Language basics

Arrays & slices

Store a sequence, sort it, and take part of it.

On this page

Array[T] is a growable list of values of type T. Slices (&[T]) are views of part of an array, and fixed arrays ([T x N]) hold a set number of items inline.

Create and grow an array#

Valk
fn main() {
    let scores = Array[int]{ 12, 8 }
    scores.append(20)
    scores.prepend(5)
    scores.insert(1, 7) !!
    println(scores.join(", "))   // 5, 7, 12, 8, 20
    println(scores.length)       // 5

    let zeros = Array[int]{ 0 x 4 }       // four zeros
    let names: Array[String] = .{ "Ada", "Sam" }
    let empty = Array[String]{}
    println("%{zeros.length} %{names.length} %{empty.length}") // 4 2 0
}

Read and change items#

Valk
fn main() {
    let items = Array[String]{ "a", "b", "c" }
    println(items[0])               // a
    println(items.get(5) !? "none") // none
    items[1] = "B"
    println(items.first() !? "")    // a
    println(items.last() !? "")     // c
    println(items.contains("B"))    // true
    println(items.index_of("c") !? 0) // 2
}

items[i] panics when i is out of range; get(i) throws a LookupError instead, so it can fall back with !?. first, last and index_of also throw when there is nothing to return.

Remove items#

Method Effect
remove(index) Remove one item, shift the rest down
swap_remove(index) Remove one item, move the last into its place (faster, changes order)
remove_value(value) Remove the first equal item
remove_where(fn) Remove every item the function accepts
pop_last(), pop_first() Remove and return an end; throws when empty
remove_duplicates() Keep the first of each value
clear() Remove everything
Valk
fn main() {
    let values = Array[int]{ 1, 2, 3, 4, 5, 2 }
    values.remove(0)
    values.remove_where(fn(v) { return v > 4 })
    values.remove_duplicates()
    let last = values.pop_last() !? 0
    println("%{values.join(",")} last=%last") // 2,3 last=4
}

Removing by index inside each over the same array skips the next item; use remove_where instead.

Loop over an array#

Valk
fn main() {
    let tasks = Array[String]{ "Build", "Test" }
    each tasks as task, index : println("%index: %task")

    let pairs = Array[(String, int)]{ ("a", 1), ("b", 2) }
    each pairs as name, count : print("%name=%count ") // a=1 b=2
    println("")
}

An array of tuples can split each item into names; see Control flow for all each forms.

Search and transform#

Valk
class User {
    name: String
    age: int
}

fn main() {
    let users = Array[User]{ User { name: "Ada", age: 36 }, User { name: "Sam", age: 20 } }
    let names = users.map(fn(u) { return u.name })
    let adults = users.filter(fn(u) { return u.age >= 30 })
    let sam = users.find(fn(u) { return u.name == "Sam" }) !? users[0]
    let oldest = users.max_by(fn(u) { return u.age }) !? users[0]
    let total_age = users.reduce(0, fn(sum, u) { return sum + u.age })
    println(names.join(", "))   // Ada, Sam
    println(adults.length)      // 1
    println("%{sam.age} %{oldest.name} %total_age") // 20 Ada 56
    println(users.any(fn(u) { return u.age > 30 })) // true
}
Method Result
map(fn) New array with each item converted
filter(fn) New array of matching items
extract(fn) Removes matching items and returns them
find(fn) First matching item; throws when none
any(fn), all(fn) bool
reduce(start, fn) Combined value
min_by(fn), max_by(fn) Item with the smallest or largest key
sum(), min(), max() For arrays of numbers
join(divider) Items as one String
merge(other), copy(), reversed(), unique() New arrays
intersect(other) Items present in both

Sort#

Valk
class User {
    name: String
    age: int
}

fn main() {
    let numbers = Array[int]{ 3, 1, 2 }
    numbers.sort()
    println(numbers.join(","))     // 1,2,3
    let descending = numbers.sorted(fn(a, b) { return a < b })
    println(descending.join(","))  // 3,2,1

    let users = Array[User]{ User { name: "Ada", age: 36 }, User { name: "Sam", age: 20 } }
    users.sort_by(fn(u) { return u.age })
    println(users[0].name)         // Sam
}
  • sort() orders numbers, strings and tuples ascending. Pass a function that returns true when a belongs after b for another order.
  • sort_by(key) sorts by a value computed per item.
  • sorted, sorted_by, reversed and shuffled return a copy; sort, sort_by, reverse and shuffle change the array.
  • Sorting is stable: equal items keep their order.

Search a sorted array#

Valk
fn main() {
    let sorted = Array[int]{ 10, 20, 30 }
    let found, position = sorted.binary_search(20)
    println("%found %position")   // true 1
    let found2, insert_at = sorted.binary_search(25)
    println("%found2 %insert_at") // false 2
}

When the value is missing, the position is where to insert it to keep the order.

Take part of an array#

A range is a start and a length: values[1 .. 2] is two items starting at index 1.

Valk
fn main() {
    let values = Array[int]{ 10, 20, 30, 40 }
    let copy = values[1 .. 2]     // new Array: 20, 30
    copy[0] = 99
    let view = &values[1 .. 2]    // view of the same items
    view[0] = 21
    println(values.join(","))     // 10,21,30,40
    println(copy.join(","))       // 99,30
}

values.range(start, length) is the same as the copy form and values.view(start, length) the same as the view form.

Slices#

A slice &[T] reads a sequence without owning it; mut &[T] may also write. A function that takes a slice accepts arrays, fixed arrays, other slices and literals:

Valk
fn total(values: local &[int]) int {
    let sum = 0
    each values as value : sum += value
    return sum
}

fn main() {
    println(total(Array[int]{ 1, 2, 3 }))  // 6
    println(total(.{ 4, 5 }))              // 9
    let buffer = [u8]{ 0 x 1024 }          // new writable slice of 1024 zero bytes
    println(buffer.length)
}

local means the function only uses the slice during the call. See Borrows & access for the rules, and Files & streams for reading into a buffer.

Fixed arrays#

[T x N] holds exactly N items, without a separate allocation. Its length cannot change:

Valk
fn main() {
    let point: [int x 3] = { 4, 7, 9 }
    point[1] = 8
    println(point.length)   // 3
    println(point[1])       // 8
    let filled: [u8 x 8] = { 0... }   // repeat the last value to the end
    println(filled[7])      // 0
}

Use [T x N]{ ... } where no type is written already, as in let key = [u8 x 2]{ 1, 2 }.