Map, Filter & Reduce

Priya Patil
5 min readMay 24, 2016

--

You can do a surprising amount in Clojure with the core functions. Three of the most useful ones are filter, map and reduce.

The main idea behind functional programming, is the ability to treat code as data. Object Oriented programming relies on the ability to save state and this means that objects and code is mutable. It has a state that can be changed and this can lead to a lot of confusion and difficulty in finding the source of a problem in a large code base.

A bit about functional programming …

In functional programming state cannot be modified (once something is made, it cannot be changed) and data is immutable which means instead of changing data, you return a whole new set of data.

This leads to thread-safe code but it also leads to more concise, possibly easier to read code. Recursion is a tool that is frequently used in the place of while loops and functions are first class citizens which is just a weird way of saying they can be passed as arguments to other functions. This is true in other languages, but for a language to be called a functional this must be true.

Map

Anyway, back to map, filter and reduce. Because of functions being first class citizens you can transform your sequence of data by applying a function to each element of it. This is what map does. It is possible to map each element of a sequence to a function you have created. This means you can transform the data you have (but really instead of changing it you create a whole new list).

(map #(* 100 %) [1 2 3])
;=> (100 200 300)

It’s possible to have multiple collections that map to each other:

(map * [1 2 3] [100 100 100])
;=> (100 200 300)

This is really useful to “alter” a set of data when you need to.

Map returns a list with the same number of elements that is given to it, it has the same number of inputs to outputs.

Filter

Filter is another useful core function that allows you to process your data and select elements that meet a certain predicate.

(filter even? [1 2 3 4 5 6 7 8 9 10])
;=> (2 4 6 8 10)

The way this would be used is not particularly different to the select/ do block in Ruby or a for/while loop in java that checks if every item in a list meets a condition adding it to another list, but instead of having to explicitly create a new list Clojure internally does it for you.

I think the Clojure version is more easy to read, but that’s just an opinion.

Filter always takes a predicate and a list and produces a new list with the same or fewer elements (a subset) after the filter has been applied.

Reduce

Reduce is the one core function out of these that I had the hardest time finding a use for in my Tic Tac Toe but eventually it made sense. The idea behind it is that you reduce a list into a single result by applying an operator/function on it.

The easiest way to see this is with a simple addition of elements of a list.

(reduce + [1 2 3 4 5 6 7 8 9 10])
;=> 55

This is how it works:

(+ (+ (+ (+ (+ (+ (+ (+ (+ 1 2) 3) 4) 5) 6) 7) 8) 9) 10)
;=> 55

It takes the first two items in the list, applies the operator to them, then takes that result along with the next item in the list and applies the operator to them and so on.

It is similar to Java’s += (add and assignment operator) which takes a value and adds to it.

It is possible to use a recursive loop to do the same thing, but reduce is a mush more elegant and simple way of doing this, if you know what reduce does.

Here is an example of doing the above as a loop:

(the starting result is 0, starting i is 0 and list is the board.

(def board [1 2 3 4 5 6 7 8 9 10])(loop [result 0 i 0 list board]
(if (= i (count list)) result
(recur (+ result (nth list i)) (inc i) board)))
;=> 55

This is a much more complex and harder to read version of reduce which ultimately does the same thing.

Reduce is especially useful if you have a hash-map and you want to accumulate values instead of overwriting them. A common way I have come across is that you can use assoc to update values of keys in a hash-map.

(reduce (fn [map string] (assoc map string (inc (map string 0))) 
{}
'{"ben", "ben", "adam", "priya" "ludmilla", "ludmilla", "ludmilla"}
;=> {"ben" 2, "adam" 1, "priya" 1, "ludmilla" 3}

This takes a map and a string and and increments the values so that they are kind of superimposed on top of each other. It says reduce the hash map into the list and apply the function to the individual values in the hash-map.

Now, there are two different types of reduce in Clojure, one that takes an initial starting argument and one that takes no starting value and applies the function to the starting values in the list.

One with no starting value:


(reduce + [1 2 3])
;=> 6

One with a starting value;

(reduce + 6 [1 2 3])
;=> 12

If you use a reduce with a starting value it’s possible to break out of it by using reduced. This is similar to a break statement in Java or Ruby. It short-circuits the reduce function and returns the accumulated value at that point.

(reduce (fn [current-value number]
(if (= 3 current-value)
(reduced current-value)
(+ current-value number)))
0
[1 1 1 6]

Here 0 is the starting value of the current value and the numbers are added together until the sum of the numbers is 3 (here it’s after the 1s) and the rest of the list isn’t added to the reduction.

Most of these functions exist in OO languages and I’ve found they have made some of my code more concise. But I better not get carried away and start using them everywhere, because sometime a good old-fashioned while loop is the way to go.

--

--