# Collections Deft ships with three persistent immutable collection types — lists, maps, and sets — plus mutable arrays for tight loops. All collection literals produce values; operations return new values rather than mutating in place (with the explicit exception of `<<` and mutable arrays). ## Literals | Type | Syntax | Example | |---|---|---| | List | `@{ ... }` | `@{ 1 2 3 }` | | Map | `%{ ... }` | `%{ :a 1 :b 2 }` | | Set | `^{ ... }` | `^{ :a :b :c }` | | Tagged map | `%Tag{ ... }` | `%User{ :name "Alice" }` | | Array (mutable) | `[array ...]` | `[array 1 2 3]` | Map keys are typically keywords (`:foo`) but can be strings, numbers, or any immutable value.
 def nums   @{ 1 2 3 4 5 }
def user   %{ :name "Alice" :roles ^{:admin :user} }
def colors ^{:red :green :blue} 
## Lists Lists are immutable persistent vectors. Random access is O(log n); head/tail operations are O(1)-ish.
 def xs @{ 10 20 30 }

$xs(len)                                    # => 3
$xs(first)                                  # => 10
$xs(last)                                   # => 30
$xs(rest)                                   # => @{ 20 30 }
$xs(nth 1)                                  # => 20
$xs(slice 0 2)                              # => @{ 10 20 }
$xs(1..3)                                   # => @{ 20 30 } — range slice, same thing
$xs(reverse)                                # => @{ 30 20 10 } 
### Construction
 def empty @{}
def from-range [range 0 5]                  # => @{ 0 1 2 3 4 }
def combined [append $xs @{ 40 50 }]         # => @{ 10 20 30 40 50 } 
### Appending `<<` mutates a local collection in place and returns the mutated collection. `[append $xs x]` returns a new collection and leaves the original alone.
 def out @{}
for x in $input { $out << $x }              # preferred for accumulation

set fresh [append $xs 99]                    # pure-functional 
See [Syntax and Style](syntax-and-style) for the full append semantics. ### Sorting and uniqueness
 [sort @{ 3 1 2 }]                            # => @{ 1 2 3 }
[sort $users { ($a~name) < ($b~name) }]      # with comparator
[unique @{ 1 1 2 3 3 }]                      # => @{ 1 2 3 }
[flatten @{ @{ 1 2 } @{ 3 4 } }]             # => @{ 1 2 3 4 } 
### Membership
 [3 in? $xs]                                  # => true
[in? $xs 3]                                  # => same, prefix form 
### Index mutation `set-nth`, `insert-nth`, and `remove-nth` return new lists:
 [set-nth $xs 0 99]                           # => @{ 99 20 30 }
[insert-nth $xs 1 15]                        # => @{ 10 15 20 30 }
[remove-nth $xs 1]                           # => @{ 10 30 } 
## Maps Maps are persistent hash maps. Lookup, assoc, and dissoc are all O(log n).
 def user %{ :name "Alice" :age 30 :role :admin }

$user~name                                   # field access (preferred)
[get $user :name]                            # equivalent
[get $user :email "n/a"]                     # with default
[user :name]                                 # IFn form — map is callable 
### Adding and removing `assoc` adds/overwrites keys; `dissoc` removes them; both return new maps:
 [assoc $user :email "alice@example.com"]     # add a key
[assoc $user :age 31]                        # overwrite
[dissoc $user :role]                         # remove

[assoc-in $user @{ :profile :name } "Al"]    # nested update
[get-in $user @{ :profile :name }]           # nested lookup (nil if missing)
[get-in $user @{ :profile :age } :unknown]   # nested lookup with default 
`merge` combines maps, with later maps winning:
 [merge $user %{ :age 31 :email "a@x.com" }] 
### Iteration
 [keys $user]                                 # => ^{:name :age :role} (as list)
[values $user]                               # => @{ "Alice" 30 :admin }
[entries $user]                              # => @{ @{:name "Alice"} ... }

for pair [entries $user] {
    echo "$pair~0 = $pair~1"
} 
When iterating a map directly, `for` and pipeline operators yield `@{key value}` pairs:
 for entry in $user { echo "$entry~0 -> $entry~1" }
$user |> each { echo "$it~0 -> $it~1" } 
### Construction helpers
 [from-entries @{ @{:a 1} @{:b 2} }]          # => %{ :a 1 :b 2 }
[zip @{ :a :b } @{ 1 2 }]                    # => %{ :a 1 :b 2 } 
## Sets Sets are persistent hash sets.
 def roles ^{:admin :user :guest}

($roles << :owner)                              # => ^{:admin :user :guest :owner}

:admin in? $roles                            # => true
[in? $roles :admin]                          # prefix form 
### Set operations
 def a ^{ 1 2 3 }
def b ^{ 2 3 4 }

[union $a $b]                                # => ^{ 1 2 3 4 }
[intersection $a $b]                         # => ^{ 2 3 }
[difference $a $b]                           # => ^{ 1 }

[subset? ^{1 2} $a]                          # => true
[superset? $a ^{1 2}]                        # => true 
## Strings as Collections Strings are `ISeq` of characters. Iteration yields single-char strings:
 "abc"(len)                                   # => 3
"abc"(first)                                 # => "a"
"abc"(rest)                                  # => "bc"

for ch in "hello" { echo $ch }
"hello" |> map { $it(upper) } |> join ""     # => "HELLO" 
`slice` and `nth` work on strings too:
 "hello"(0..3)                                # => "hel"
"hello"(nth 1)                               # => "e" 
## Higher-Order Operations All collections support the functional trio via `ISeq` / `IReducible`:
 $xs |> map { $it * 2 }                       # lazy seq — see Lazy Sequences
$xs |> filter { ($it % 2) == 0 } |> collect
$xs |> reduce 0 { ($acc + $it) }
$xs |> find { $it > 20 }                     # => first match, or nil
$xs |> any? { $it > 20 }                     # => true if any matches
$xs |> all? { $it > 0 }                      # => true if all match
$xs |> group { ($it % 2) == 0 }              # => %{ :true @{...} :false @{...} } 
For the functional deep dive, see [Lazy Sequences](lazy-sequences) and [Transducers](transducers). ## Mutable Arrays When you need real mutation in a hot loop, mutable arrays skip the persistence overhead:
 def arr [array 1 2 3]
[aset $arr 0 99]                             # mutates in place, returns nil
[aget $arr 0]                                # => 99 
Mutable arrays don't implement the full collection protocol; use them when performance matters and convert back to a list with `collect` when you're done. ## Pretty-Printing `pretty` prints a structured view; `pretty-str` returns it as a string:
 [pretty $user]
# %{
#   :name "Alice"
#   :age 30
#   :role :admin
# }

set text [pretty-str $user 4]                # indent width 4 
## Common Pitfalls - **`$x(len)` vs `[len $x]`** — both work; postfix is preferred for single-arg receiver calls. - **`get` with default** — `[get $m key default]`; the default is returned only when the key is absent. - **Map iteration order** — keys/values/entries return in insertion order; the order is deterministic for a given sequence of operations but not alphabetically sorted. - **Set literals with duplicate keys** — `^{ :a :a :b }` collapses to `^{ :a :b }`. - **Field access on a missing key** — `$m~missing-key` returns `nil` rather than throwing. ## See Also - [Lazy Sequences](lazy-sequences) — how `map`/`filter` work - [Transducers](transducers) — reusable transformation pipelines - [Pattern Matching](pattern-matching) — destructuring collections in `match` - [Stdlib](stdlib) — full list of collection operations