The Core Sequence Trio: map, filter, reduce
map, filter, and reduce are the three foundational sequence-processing functions in Clojure, and together they let you express most data transformations without writing explicit loops. map applies a function to every element and returns a new lazy sequence of results; filter keeps only elements satisfying a predicate; reduce collapses a sequence into a single accumulated value by repeatedly applying a two-argument function.
Cricket analogy: A scorer who goes through every ball, converting raw deliveries into run values with map, then only keeps boundary balls with filter, then sums them for a boundary count with reduce, demonstrates the trio in a single innings analysis.
map: Transforming Every Element
map takes a function and one or more collections and returns a lazy sequence of applying that function positionally across them; passing multiple collections lets you zip and transform in one step, and map stops as soon as the shortest input is exhausted. Because the result is lazy, (map f coll) does no work until something forces (realizes) the sequence, such as printing it or wrapping it in doall.
Cricket analogy: Pairing each ball of an over with the bowler's intended line, a multi-collection map of deliveries and lines, produces a combined analysis row per ball, just like (map + coll1 coll2).
(map inc [1 2 3]) ;=> (2 3 4)
(map + [1 2 3] [10 20 30]) ;=> (11 22 33)
(map str ["a" "b"] ["x" "y"]) ;=> ("ax" "by")filter: Selecting Elements
filter takes a predicate and a collection and returns a lazy sequence containing only the elements for which the predicate returns truthy; remove is filter's mirror image, keeping elements the predicate rejects. Because Clojure treats nil and false as the only falsy values, predicates can return any truthy value (not just true/false) and filter will still work correctly.
Cricket analogy: A selector keeping only bowlers with an economy rate under 6 from the full squad list is filter applied to player stats.
(filter even? [1 2 3 4 5 6]) ;=> (2 4 6)
(filter #(> % 10) [5 15 8 20]) ;=> (15 20)
(remove even? [1 2 3 4 5 6]) ;=> (1 3 5)filter and map are lazy — wrapping side-effecting functions (like println) inside them can appear to do nothing until the sequence is realized by something like doall, into, or printing it. Chunked sequences can also realize more elements than expected, causing side effects to fire in unexpected batches.
reduce: Accumulating a Result
reduce walks a collection left to right, repeatedly calling a two-argument function with an accumulator and the next element, and returns the final accumulated value; supplying an explicit initial value avoids surprises when the collection is empty or has one element. Because map and filter are really special cases of the fold pattern reduce embodies, understanding reduce deeply — including reduced for early termination — unlocks writing your own sequence-processing HOFs.
Cricket analogy: Running total of a team's score, ball by ball, where each delivery's runs get added to a rolling accumulator, is exactly reduce with + as the combining function.
(reduce + 0 [1 2 3 4]) ;=> 10
(reduce (fn [acc x] (if (> x 100) (reduced acc) (+ acc x)))
0 [10 20 30 200 40]) ;=> 60
(reduce conj [] [1 2 3]) ;=> [1 2 3]reduce is eager, unlike map and filter — calling reduce forces the entire (possibly lazy) input sequence to be realized in order to compute the final result.
- map applies a function across one or more collections positionally and returns a lazy sequence of results.
- filter keeps only elements for which a predicate returns truthy; remove keeps the opposite.
- reduce folds a collection into a single accumulated value using a two-argument combining function.
- map and filter are lazy — nothing is computed until the sequence is realized.
- Providing an explicit initial value to reduce avoids edge-case bugs with empty or single-element collections.
- reduced allows early termination from inside a reduce without processing the rest of the collection.
- map, filter, and reduce compose naturally into multi-stage data-processing pipelines.
Practice what you learned
1. What does (map + [1 2 3] [10 20 30]) return?
2. What is the key difference between filter and remove?
3. Why is it good practice to supply an initial value to reduce?
4. What does (reduced acc) do inside a reduce function?
5. Which function would you use to keep only strings longer than 5 characters from a vector?
Was this page helpful?
You May Also Like
Higher-Order Functions in Clojure
Learn how Clojure treats functions as first-class values, letting you pass them as arguments, return them from other functions, and compose new behavior with comp, partial, and complement.
Lazy Sequences
Understand how Clojure's lazy sequences defer computation until it's needed, enabling infinite sequences, and learn the chunking and memory pitfalls that trip up beginners.
Threading Macros
Learn how ->, ->>, some->, some->>, and as-> rewrite deeply nested Clojure expressions into readable, linear pipelines without changing runtime behavior.
Related Reading
Related Study Notes in Programming
Browse all study notesApache Spark Study Notes
Programming · 30 topics
ProgrammingApache Flink Study Notes
Programming · 30 topics
ProgrammingHadoop Study Notes
Programming · 30 topics
ProgrammingSnowflake Study Notes
Programming · 30 topics
ProgrammingApache Airflow Study Notes
Programming · 30 topics
Programmingdbt (Data Build Tool) Study Notes
Programming · 30 topics