Skip to main content

Data Structures

Progress 0/13

Phel ships with four go-to collections: vectors, maps, sets, and lists. They share two superpowers - immutability (every "change" returns a new value) and structural sharing (so it stays fast). Let's meet them.

Exercise 1 easy

Build a vector with the elements 2, "nice", and true.

[2 "nice" true]
; or
(vector 2 "nice" true)

Vectors are ordered collections that hold any mix of types. Square brackets are the idiomatic literal.

Learn more: Data Structures

Exercise 2 easy

Build a vector containing the keywords :hello and :world.

[:hello :world]

Keywords are lightweight identifiers prefixed with :. They're cheap, comparable by identity, and perfect for map keys or enum-like tags.

Learn more: Basic Types

Exercise 3 easy

Build a map with keys :name and :age, values "Ada" and 36.

{:name "Ada" :age 36}
; or
(hash-map :name "Ada" :age 36)

Maps are key-value collections. Keyword keys are the idiomatic default in Phel.

Learn more: Data Structures

Exercise 4 easy

Build a set from 1, 2, 3, 2. How many elements does it hold?

(hash-set 1 2 3 2)
; => #{1 2 3} - duplicates dropped

(count (hash-set 1 2 3 2))
; => 3

Sets store unique values. Adding a duplicate is a no-op.

Learn more: Data Structures

Exercise 5 easy

Use count to find the size of [10 20 30] and {:a 1 :b 2}. Then check (empty? []) and (empty? [1]).

(count [10 20 30])    ; => 3
(count {:a 1 :b 2})   ; => 2
(empty? [])           ; => true
(empty? [1])          ; => false

count works on every Phel collection (and strings). empty? is the predicate version of "is the count zero?".

Learn more: Data Structures

Exercise 6 easy

Use get to read the second element from [10 20 30].

(get [10 20 30] 1)
; => 20

Vector indices are zero-based, so index 1 is the second element.

Learn more: Data Structures

Exercise 7 easy

Given this map, retrieve :name three different ways:

(def person {:name "Ada" :age 36})
(def person {:name "Ada" :age 36})

(get person :name) ; => "Ada"  - explicit
(person :name)     ; => "Ada"  - map as function
(:name person)     ; => "Ada"  - keyword as function

All three are equivalent. The keyword-as-function form ((:name person)) is the most idiomatic style.

Learn more: Data Structures

Exercise 8 easy

Use get-in to grab :treasure from this nested structure:

(def dungeon {:description "dark cave"
              :rooms [{:contents :monster}
                      nil
                      {:contents [:trinket :treasure]}]})
(def dungeon {:description "dark cave"
              :rooms [{:contents :monster}
                      nil
                      {:contents [:trinket :treasure]}]})

(get-in dungeon [:rooms 2 :contents 1])
; => :treasure

get-in walks nested maps and vectors via a path of keys/indices.

Learn more: Data Structures

Exercise 9 easy

Use assoc to add :email "ada@example.com" to person. What does assoc return? What is person after?

(def person {:name "Ada" :age 36})
(def person {:name "Ada" :age 36})

(assoc person :email "ada@example.com")
; => {:name "Ada" :age 36 :email "ada@example.com"}

person
; => {:name "Ada" :age 36}  - unchanged!

Phel's data structures are immutable. assoc returns a fresh map; the original stays as it was. This is the foundation of safe, predictable code.

Learn more: Data Structures

Exercise 10 easy

Use conj to append 4 to [1 2 3]. Verify the original length didn't change.

(def nums [1 2 3])
(def more-nums (conj nums 4))

(count nums)      ; => 3 (original untouched)
(count more-nums) ; => 4
more-nums         ; => [1 2 3 4]

Same immutability story: conj returns a new vector, the original stays put.

Learn more: Data Structures

Exercise 11 easy

Use contains? to check whether the set (hash-set :apple :banana :cherry) has :banana. Then check :grape.

(def fruits (hash-set :apple :banana :cherry))

(contains? fruits :banana) ; => true
(contains? fruits :grape)  ; => false

contains? is the natural way to ask "is this here?" - works on sets and on map keys.

Learn more: Data Structures

Exercise 12 easy

Inspect {:a 1 :b 2 :c 3} with keys and values.

(keys {:a 1 :b 2 :c 3})   ; => [:a :b :c]
(values {:a 1 :b 2 :c 3}) ; => [1 2 3]

Useful when you only care about one side of a map.

Learn more: Data Structures

Exercise 13 easy

Use merge to combine {:name "Ada"} and {:age 36 :role :admin} into one map.

(merge {:name "Ada"} {:age 36 :role :admin})
; => {:name "Ada" :age 36 :role :admin}

merge builds a new map containing all entries. Later values win on conflicts - great for layering defaults with overrides.

Learn more: Data Structures