09 — Collections
Chapter goal: store and process data with lists, dicts, sets and tuples. Reference:
../language-reference/expressions.md§14, §16. Example:../../examples/tour.aura.
Literals
let list = [1, 2, 3]
let set = {1, 2, 3}
let dict = {name: "Alice", age: 30}
let pair = (1, "hello")
let single = (42,)
let empty = ()
- Lists use
[ ... ]. - Sets use
{ ... }with no colon. - Dicts use
{ key: value }. Access by indexd["k"]or by memberd.kboth work. - Tuples need the comma:
(42,)is a one-element tuple;(42)is just42.
A trailing comma is allowed everywhere. Dicts preserve insertion order (CPython 3.7+).
Indexing and slicing
let numbers = [10, 20, 30, 40]
print(numbers[0]) // 10
print(numbers[-1]) // 40 — negative index
print(numbers[1:3]) // [20, 30]
print(numbers[::2]) // [10, 30]
A range used as an index is a slice:
print(numbers[0..<2]) // [10, 20] — exclusive upper bound
print(numbers[0..2]) // [10, 20, 30] — inclusive
print(numbers[1..]) // [20, 30, 40] — open end
Length, membership and mutation
let numbers = [1, 2, 3]
numbers.add(4) // append
print(numbers.length()) // 4
print(numbers.size()) // 4
print(numbers.contains(2)) // true
print(numbers.is_empty()) // false
print(2 in numbers) // true
.length() / .size() → len(x); .contains(x) → x in x; .add(x) → append. A user-declared method with one of those names wins over the convenience.
Unknown member calls pass through to Python verbatim: .append, .pop, .sort, .reverse, .insert, .remove, .index, .count, .extend, .clear.
Dicts and sets
let user = {name: "Alice", age: 30}
print(user["name"]) // Alice
print(user.age) // 30
let unique = {1, 2, 2, 3, 3, 3}
print(unique) // {1, 2, 3}
Dict keys can be written unquoted when they are identifiers: {name: "Alice"}.
Spread
let list1 = [1, 2]
let list2 = [3, 4]
let combined = [*list1, *list2, 5] // [1, 2, 3, 4, 5]
let defaults = {a: 1, b: 2}
let overrides = {b: 9}
let merged = {**defaults, **overrides} // {a: 1, b: 9}
A bare spread in a value position is a parse error; spread is allowed only in a literal or a call argument.
Comprehensions
Comprehensions build a collection from an iterable, with optional filters:
let squares = [x * x for x in range(6)]
let evens = [x for x in range(20) if x % 2 == 0]
let lengths = {w: w.length() for w in ["ab", "xyz"]}
let unique_mods = {x % 3 for x in range(10)}
[0, 1, 4, 9, 16, 25]
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
{'ab': 2, 'xyz': 3}
{0, 1, 2}
Multiple for and if clauses are supported and preserve source order. Comprehensions are eager; a generator expression (e for x in xs) is lazy.
Strings
Strings are Unicode and immutable, with Aura-named convenience methods that map to Python:
let text = "Hello, World"
print(text.to_upper()) // HELLO, WORLD
print(text.to_lower()) // hello, world
print(text.starts_with("He")) // true
print(text.ends_with("ld")) // true
print(text.trim()) // trims whitespace
print(text.slice(1, 4)) // ell
print(text.char_at(0)) // H
print(text.length()) // 12
Aura aliases include starts_with/ends_with, to_upper/to_lower, trim/trim_left/trim_right, index_of, is_alpha, is_digit and more.
Passing collections to functions
Collections are reference values: mutating one inside a function is visible to the caller, while reassigning the parameter is not.
def mutate(xs) { xs.add(9) }
def reassign(x) { x = 9 }
def main() {
let a = [1]
mutate(a)
print(a) // [1, 9]
let n = 1
reassign(n)
print(n) // 1
}
What you learned
- Lists, dicts, sets, tuples; indexing, slices, and range-as-slice.
- Length/membership/mutation conveniences and Python passthrough.
- Spreads and comprehensions.
- Collections are references; numbers and strings are values.