Reduce in Clojure
reduce is one of the most powerful functions in Clojure’s core library. It’s a fundamental building block for data transformation and serves as the foundation for many higher-level operations. In this guide, we’ll explore how `reduce` works and demonstrate its versatility through practical examples.
Basic Concepts
At its core, `reduce` processes a collection by applying a function to pairs of values to produce a single result. The function signature is:
(reduce f coll) ; Using initial value from collection
(reduce f init coll) ; Using explicit initial valueWhen you call `reduce`, it:
1. Takes the first two items from the collection (or the initial value and first item)
2. Applies the reducing function to them
3. Uses that result with the next item
4. Continues until the collection is exhausted
Common Usage Patterns
1. Basic Numeric Operations
The simplest use of `reduce` is for numeric calculations:
;; Sum numbers
(reduce + [1 2 3 4 5]) ; -> 15
;; Step by step:
;; 1 + 2 = 3
;; 3 + 3 = 6
;; 6 + 4 = 10
;; 10 + 5 = 15
;; Multiply numbers
(reduce * [1 2 3 4 5]) ; -> 1202. Working with Collections
`reduce` excels at building and transforming collections:
;; Combining vectors
(reduce into [] [[1 2] [3 4] [5 6]])
; -> [1 2 3 4 5 6]
;; Building maps
(reduce (fn [acc [k v]]
(assoc acc k (inc v)))
{}
{:a 1 :b 2 :c 3})
; -> {:a 2 :b 3 :c 4}3. Advanced Patterns
Counting Occurrences
(reduce (fn [counts item]
(update counts item (fnil inc 0)))
{}
[:a :b :a :c :b :a])
; -> {:a 3 :b 2 :c 1}Filter and Transform in One Pass
(reduce (fn [result item]
(if (even? item)
(conj result (* item 2))
result))
[]
[1 2 3 4 5 6])
; -> [4 8 12]4. Early Termination
Get Vasanth’s stories in your inbox
Join Medium for free to get updates from this writer.
Sometimes you want to stop reducing early when a condition is met:
(reduce (fn [acc x]
(if (> x 3)
(reduced acc)
(conj acc x)))
[]
[1 2 3 4 5])
; -> [1 2 3]5. Working with Nested Structures
`reduce` can handle complex nested data structures:
;; Sum nested values
(reduce (fn [sum m]
(+ sum (get-in m [:stats :value] 0)))
0
[{:stats {:value 10}}
{:stats {:value 20}}
{:other "data"}])
; -> 306. Building Tree Structures
(reduce (fn [tree [parent child]]
(update tree parent (fnil conj #{}) child))
{}
[[:root :a]
[:a :b]
[:a :c]
[:b :d]])
; -> {:root #{:a}
; :a #{:b :c}
; :b #{:d}}Special Variants
reduce-kv
For key-value collections, reduce-kv provides index/key access:
(reduce-kv (fn [acc idx item]
(assoc acc idx (str item "!")))
[]
["a" "b" "c"])
; -> ["a!" "b!" "c!"]reductions
If you need all intermediate values:
(reductions + [1 2 3 4])
; -> (1 3 6 10)Key Implementation Details
1. Initial Value Behavior
— Without an initial value, the first element becomes the initial value
— Empty collection with no initial value throws an exception
— Empty collection with initial value returns the initial value
2. Performance Considerations
— More efficient than recursive approaches for large collections
— Processes items sequentially
— Works well with lazy sequences
Common Gotchas and Tips
1. Always provide an initial value when the reducing function expects certain types
2. Use type hints for better performance in numeric operations
3. Consider using transducers for complex transformations
4. Be careful with lazy sequences— reduce will realize the entire sequence
Conclusion
reduce is a versatile function that can handle everything from simple accumulation to complex data transformations. Understanding its patterns and proper usage will make your Clojure code more expressive and efficient.
Remember that while reduce is powerful, sometimes using specialized functions like map, filter, or group-by can make your code more readable. Choose the right tool for your specific use case.







