Collections
Lists and maps are Risor's two collection types. Both support indexing, slicing, and functional methods for iteration. Without loops, these methods are how you process data.
Lists
Ordered, mutable, and heterogeneous. Lists hold values of any type:
let items = [1, 2, 3]
let mixed = ["hello", 42, true, nil]
let empty = []Indexing
Access elements by zero-based index:
let items = [10, 20, 30, 40, 50]
items[0] // 10
items[2] // 30Negative indices count from the end:
items[-1] // 50
items[-2] // 40Slicing
Slice syntax [start:end] extracts a sub-list. Start is inclusive, end is
exclusive:
let items = [10, 20, 30, 40, 50]
items[1:3] // [20, 30]
items[:2] // [10, 20]
items[2:] // [30, 40, 50]Maps
Maps hold unordered key-value pairs with string keys:
let person = {name: "Alice", age: 30}
let empty = {}Accessing Values
Values can be accessed with dot notation or bracket notation:
let m = {a: 1, b: 2}
m.a // 1
m["a"] // 1Bracket notation is required when the key is stored in a variable:
let key = "a"
m[key] // 1Membership Testing
Use the in operator to check whether a key exists in a map:
"name" in person // true
"email" in person // falseSpread Expressions
The spread operator ... expands a collection into its elements.
Spread a list into a new one:
let a = [1, 2]
let b = [...a, 3, 4] // [1, 2, 3, 4]Merge maps together:
let merged = {...{a: 1}, ...{b: 2}} // {a: 1, b: 2}Spread arguments into a function call:
let args = [1, 2, 3]
fn(...args)Destructuring
Pull values out of collections and bind them to variables in one step.
Map Destructuring
let {name, age} = {name: "Alice", age: 30}
// name is "Alice", age is 30Provide default values for missing keys:
let {x = 5} = {}
// x is 5List Destructuring
let [first, second] = [1, 2]
// first is 1, second is 2Iterator Protocol
Maps, ranges, and other types produce lazy iterators that implement the Enumerable interface. Values are generated on demand rather than materialized into full collections up front.
Map Iterators
let m = {a: 1, b: 2}
let k = m.keys() // lazy iterator
list(k) // ["a", "b"]
[...m.values()] // [1, 2]Use .entries() to iterate over key-value pairs:
[...m.entries()] // [[k1, v1], [k2, v2], ...]Range Iterators
range produces a lazy sequence of integers:
list(range(5)) // [0, 1, 2, 3, 4]
range(5).each(i => nil) // iterate through valuesConsuming Iterators
Iterators can be consumed in two ways:
- Call
list()to collect all values into a list - Use the spread operator
[...]to expand into a list literal
let values = list(m.values())
let keys = [...m.keys()]Functional Iteration
No loops means functional methods are the standard toolkit for processing collections:
let numbers = [1, 2, 3, 4, 5]
// Transform each element
numbers.map(x => x * 2) // [2, 4, 6, 8, 10]
// Keep elements matching a condition
numbers.filter(x => x > 3) // [4, 5]
// Accumulate a single result
numbers.reduce(0, (acc, x) => acc + x) // 15
// Run a function for each element
let total = 0
numbers.each(x => { total += x })Combine them with pipes for multi-step transformations that read top to bottom:
let evens = [1, 2, 3, 4, 5].filter(x => x % 2 == 0)
evens.map(x => x * x)