Collections

functions and helpers for manipulating and dealing with different types of collections (blocks, dictionaries, and strings)


Functions

Predicates


The Collections module provides a unified interface for working with different types of collections: blocks, dictionaries, and strings. Most operations work seamlessly across all collection types, making it easy to manipulate data regardless of its structure.

Key Concepts

  • Unified operations across blocks, dictionaries, and strings
  • Support for both mutable and immutable operations
  • Efficient array/block manipulation
  • Ordered dictionary operations
  • Lazy evaluation with ranges
  • Flexible combination and permutation generation

Basic Usage

Creating Collections

; blocks (arrays)
numbers: [1 2 3 4 5]
mixed: ["hello" 42 true]

; dictionaries (ordered hash tables)
user: #[
    name: "John"
    age: 30
    city: "London"
]

; ranges (lazy collections)
evens: 2..10

; use `@` (or `array`) to "evaluate" a range
print @evens     ; 2 3 4 5 6 7 8 9 10

Note
Dictionaries in Arturo maintain insertion order, unlike hash tables in many other languages.

Basic Operations

lst: [1 2 3]

; adding/removing elements
'lst ++ 4           ; append
'lst -- 2           ; remove value
'lst ++ [5 6]       ; extend

print lst           ; 1 3 4 5 6

; accessing elements
print first lst     ; 1
print last lst      ; 6

; accessing the element at index: 2
; (= the third element in our list)
print lst\2         ; 4

Important
Using the literal form (e.g., 'lst) modifies the collection in place, while using the value directly returns a new collection.

Collection Indexing

; basic array indexing
colors: ["red" "green" "blue"]
print colors\0              ; red (first element)
print colors\2              ; blue (last element)

; dynamic indexing with expressions
pos: 1
print colors\[pos]          ; green

; dictionary field access
user: #[
    name: "John"
    surname: "Doe"
    age: 30
]

print user\name             ; John

; dynamic dictionary field access
field: 'surname             ; using a literal
print user\[field]          ; Doe

Note
Indexing with \ works uniformly across all collection types. Use a block after \ when you need to evaluate an expression to get the index or field name.

Collection Information

data: [1 2 2 3 3 3]

size data           ; => 6
empty? data         ; => false
contains? data 2    ; => true
index data 3        ; => 2

Common Patterns

Collection Transformation

; flatten nested structure
nested: [[1 2] [3 4] [5 6]]
flat: flatten nested
; [1 2 3 4 5 6]

; unique elements
duplicates: [1 2 2 3 3 3]
print unique duplicates     ; 1 2 3

; reversing
print reverse [1 2 3]       ; 3 2 1

Working with Dictionaries

user: #[
    name: "John"
    age: 30
]

; accessing data
keys user     ; => ["name" "age"]
values user   ; => ["John" 30]

; extending
additional: #[
    city: "London"
    job: "Developer"
]
complete: extend user additional
print complete
; [name:John age:30 city:London job:Developer]

Working with Strings

text: "Hello 世界"      ; unicode string

; strings are like character arrays
print text\0           ; H
print text\4           ; o
print text\5           ; [space]
print text\6           ; 世

; get size (in characters, not bytes)
print size text        ; 8

; iterate through characters
loop text 'c ->
    print ["char:" c]
; char: H 
; char: e 
; char: l 
; char: l 
; char: o 
; char:   
; char: 世 
; char: 界

Note
Strings in Arturo are always Unicode-aware. When indexing or iterating, you're working with actual characters, not bytes.

Combinations & Permutations

items: [1 2 3]

; specific size combinations
combine.by:2 items 
; => [[1 2] [1 3] [2 3]]

; permutations
permutate items    
; => [[1 2 3] [1 3 2] [2 1 3] [2 3 1] [3 1 2] [3 2 1]]

Tip
Use the .repeated attribute with combine or permutate when you want to allow elements to be used multiple times.

Efficient Range Operations

; create a range of numbers
nums: 1..1000

; work with it without generating all values
first nums          ; => 1
last nums           ; => 1000
nums\42             ; => 42

Note
Ranges are lazy - they don't generate all values until needed, making them very memory-efficient for large sequences.

Advanced Examples

Working with Nested Data
users: #[
    active: @[
        #[name: "John" score: 42 tags: ["admin" "dev"]]
        #[name: "Alice" score: 28 tags: ["dev"]]
        #[name: "Bob" score: 35 tags: ["admin"]]
    ]
    inactive: @[
        #[name: "Eve" score: 15 tags: ["guest"]]
    ]
]

; find all admins with scores > 30
admins: select users\active 'user -> 
    and? [contains? user\tags "admin"]
         [user\score > 40]

print map admins 'usr -> 
    usr\name     
; John

; get unique tags across all users
allTags: unique flatten map users\active 'usr -> usr\tags    
; admin dev
Managing a Priority Queue
tasks: []

; add tasks with priorities
'tasks ++ #[priority: 3 task: "Low priority"]
'tasks ++ #[priority: 1 task: "Urgent!"]
'tasks ++ #[priority: 2 task: "Important"]

; sort by priority
sorted: arrange tasks 'item -> item\priority
loop sorted 'item ->
    print [item\priority ":" item\task]
; 1 : Urgent!
; 2 : Important
; 3 : Low priority