Clojure Sequence API Cheatsheet
First we need to define a few shape symbols so we can use them in the examples that follow.
(def ■ ■)
(def ▲ ▲)
(def ● ●)
Now we can take a little tour of some of the most common functions in the Clojure sequence API. If any of them don't quite make sense to you, edit the examples and re-run the cells until you get an intuition for them. 😊
These first ones are mostly pretty self-explanatory:
(first [● ■ ▲])
(second [● ■ ▲])
To be honest, I wish the standard library had a third
, fourth
, and fifth
too. But we can make do with nth
:
(nth [● ■ ▲] 2) ; indexed from 0, of course!
(rest [● ■ ▲])
(last [● ■ ▲])
(butlast [● ■ ▲]) ; every element but (the) last
I guess everybody reading this has used map
by now, but as a reminder: it just calls your function on every element and returns the result of so doing in a new list. Here we demonstrate that by testing each element to see if it's a ■ , returning a new list of true
and false
values. 😊
(map (partial = ■) [■ ● ■ ▲])
These two functions, filter
and remove
, are each the other's complement. Use filter
when you want to keep elements based on some predicate and remove
to do the opposite.
(filter (partial = ■) [■ ● ■ ▲])
(remove (partial = ■) [■ ● ■ ▲])
We all need to de-duplicate a collection from time to time, for which there's the ever-useful distinct
(named the same as the SQL clause).
(distinct [■ ● ■ ▲ ● ▲])
Agile little interpose skips between the elements of its second argument, depositing one of your first argument between each pair:
(interpose ▲ [● ● ●])
There are many used for interpose
, but the one I probably use most often is nicely formatting a lists of strings, like this:
(apply str (interpose ", " ["cinnamon" "spice" "everything nice"]))
Sometimes you want to combine the elements of multiple sequences into a new sequence in a round-robin way. In this situation, interleave
has you covered:
(interleave [■ ■ ■] [▲ ▲ ▲] [● ● ●])
Taking and dropping work so much like you'd describe the operations in English that I'll just let them speak for themselves:
(take 3 [■ ■ ● ● ▲ ▲])
(take-nth 2 [■ ■ ● ● ▲ ▲ ■ ■])
(take-while (partial = ■) [■ ■ ● ● ▲ ▲ ■ ■])
(drop-while (partial = ■) [■ ■ ● ● ▲ ▲ ■ ■])
When programming in a functional style over sequences, partition
is a wonderful help. In the simple case, maybe you just need to split up a sequence into chunks:
(partition 2 [■ ■ ● ● ▲ ▲ ■ ■])
But you can often find you need to look for a pair of tokens in a sequence. If you don't have a function like partition, you might be tempted to write something fairly complex to track the state of the previous and current elements in a loop
. But partition has a special power when you tell is to advance one element at a time, like a sliding window over a sequence:
(partition 2 1 [■ ■ ● ● ▲ ▲ ■ ■])
Which lets you do things like this, which returns all of the pairs where a ▲ is the first element:
(filter (= (first %) ▲) (partition 2 1 [■ ■ ● ● ▲ ▲ ■ ■]))
Another extremely useful sequence partitioning function is partition-by
, which splits a sequence whenever the return value of its predicate changes. Here, for example, it divides the input sequence into three parts — before, during, and after the central pair of ▲s:
(partition-by (partial = ▲) [■ ■ ● ● ▲ ▲ ■ ■])
One common use of partition-by
is grouping runs of the same element in a sequence with identity:
(partition-by identity [■ ■ ● ● ▲ ▲ ■ ■])
A straightforward tool is our friend split-at,
cleaving a sequence in two at the provided index:
(split-at 3 [■ ■ ● ● ▲ ▲ ■ ■])
I find it particularly useful in the context of destructuring:
(let [[first-three others] (split-at 3 [■ ■ ● ● ▲ ▲ ■ ■])]
{:first-three first-three
:others others})
A powerhouse for quickly knowing something about the distribution of values in a sequence, I reach for frequencies
all the time:
(frequencies [■ ■ ● ● ▲ ▲ ■ ■])
These little fellows are just plain useful. Ask questions anout a whole sequence with any predicate:
(some (partial = ●) [■ ● ■ ▲])
(every? (partial = ●) [■ ● ■ ▲])
(every? (partial = ●) [● ● ●])
Another incredible time saver is group-by
. Given a sequence and a predicate, it returns a sequence of maps from the return value of a predicate to items for which the predicated returned that value. That's sort of a mouth full, so here's an example of building an index for some triples based on the first item in each one:
(group-by first [[■ ● ●] [● ▲ ▲] [▲ ■ ■]])