Skip to content
AgileLogics
7 min read

The Sequence Library

Prerequisites: Sequences and laziness

Clojure's core has around a hundred sequence functions. These are the ones that show up in everyday code.

Grouping and counting

(def words ["apple" "avocado" "banana" "blueberry" "cherry" "apple"])

(println (frequencies words))
(println (group-by count words))
(println (update-vals (group-by first words) count))
(println (distinct words))
(println (dedupe [1 1 2 2 1]))
{apple 2, avocado 1, banana 1, blueberry 1, cherry 1}
{5 [apple apple], 7 [avocado], 6 [banana cherry], 9 [blueberry]}
{a 3, b 2, c 1}
(apple avocado banana blueberry cherry)
(1 2 1)

Partitioning

(println (partition 3 (range 10)))
(println (partition-all 3 (range 10)))
(println (partition 3 1 [1 2 3 4 5]))          ; sliding window
(println (partition-by even? [1 3 2 4 5 7 6]))
(println (split-at 2 [:a :b :c :d]))
(println (split-with neg? [-2 -1 0 1 -5]))
((0 1 2) (3 4 5) (6 7 8))
((0 1 2) (3 4 5) (6 7 8) (9))
((1 2 3) (2 3 4) (3 4 5))
((1 3) (2 4) (5 7) (6))
[(:a :b) (:c :d)]
[(-2 -1) (0 1 -5)]

partition drops an incomplete last group; partition-all keeps it.

Sorting

(def people [{:name "Ada" :age 36} {:name "Linus" :age 28} {:name "Grace" :age 36}])

(println (map :name (sort-by :age people)))
(println (map :name (sort-by :age > people)))
(println (map :name (sort-by (juxt :age :name) people)))
(println (sort > [3 1 2]))
(println (sort-by count ["ccc" "a" "bb"]))
(println (:name (apply max-key :age people)))
(Linus Ada Grace)
(Ada Grace Linus)
(Linus Ada Grace)
(3 2 1)
(a bb ccc)
Grace

sort-by is stable, and juxt gives multi-key sorting. max-key and min-key pick the item with the largest or smallest key.

Combining and reshaping

(println (mapcat (fn [x] [x x]) [1 2 3]))
(println (interleave [:a :b :c] [1 2 3]))
(println (apply str (interpose ", " ["x" "y" "z"])))
(println (zipmap [:a :b :c] [1 2 3]))
(println (into {} [[:a 1] [:b 2]]))
(println (into #{} [1 2 2 3]))
(println (flatten [1 [2 [3 [4]]]]))
(println (reductions + [1 2 3 4]))
(println (take-while #(< % 4) [1 2 3 4 1]))
(println (drop-while #(< % 4) [1 2 3 4 1]))
(1 1 2 2 3 3)
(:a 1 :b 2 :c 3)
x, y, z
{:a 1, :b 2, :c 3}
{:a 1, :b 2}
#{1 3 2}
(1 2 3 4)
(1 3 6 10)
(1 2 3)
(4 1)
  • mapcat maps, then concatenates the results.
  • zipmap builds a map from keys and values; into pours any sequence into any collection.
  • reductions shows every intermediate step of a reduce.

Putting it together

(def orders
  [{:customer "ada" :amount 30}
   {:customer "bob" :amount 15}
   {:customer "ada" :amount 20}
   {:customer "cy" :amount 70}])

(->> orders
     (group-by :customer)
     (map (fn [[customer os]] [customer (reduce + (map :amount os))]))
     (sort-by second >)
     (run! (fn [[customer total]] (println customer total))))
cy 70
ada 50
bob 15

run! calls a function on every item for its side effects — the eager, pipeline-friendly cousin of doseq.