I could safely say that map
, reduce
and filter
are the most commonly used functions in Clojure
(and probably other functional languages). Of course Clojure
provides other useful functions that operate on collections that are not so popular and commonly known. Let’s look at some of them.
keep
Let’s start with the keep
function. Its idea is simple, it takes function f
as a first parameter and a collection as second:
(keep f coll)
The result is a lazy sequence of results of applying f
to every item in this collection - (f item)
. There is only one important condition, if result of (f item)
gives nil
then this item isn’t in returned sequence. On the other hand (in contrast to the filter
function), if (f item)
returns false
it appears in result.
Let’s look at the example:
(keep :a '({:a 1} {:b 3} {:a 8} {:z 7}))
=> (1 8)
Here we use :a
keyword as a function (it implements IFn
interface) on collection of maps. The result is a sequence with 1
and 8
, because (:a {:a 1})
evaluates to 1
and (:a {:a 8})
returns 8
. The (:a {:b 3})
and (:a {:z 8})
evaluates to nil
, so they aren’t in the final result.
For comparison if we use filter
we get such result:
(filter :a '({:a 1} {:b 3} {:a 8} {:z 7}))
=> ({:a 1} {:a 8})
and map
behaves in this way:
(map :a '({:a 1} {:b 3} {:a 8} {:z 7}))
=> (1 nil 8 nil)
keep-indexed
We can say that keep-indexed
is extended version of keep
function. The difference is that the function f
that we pass as a first argument must have such signature:
(fn [index item])
where index
will get succeeding non-negative integers starting with 0
:
(keep-indexed
(fn [index item]
[index (:a item)])
'({:a 1} {:b 3} {:a 8} {:z 7}))
=> ([0 1] [1 nil] [2 8] [3 nil])
and one more example (that have more sense):
(keep-indexed
(fn [index item]
(when (even? index)
(:a item)))
'({:a 1} {:a 5} {:b 3} {:a 8} {:a 12} {:z 7}))
=> (1 12)
Here we want to return only values of keyword :a
in the map if the map is on the even position (like: 0, 2, 4, 6 and so on) in the collection and has this keyword.
map-indexed
The map-indexed
function is equivalent of keep-indexed
function, but of course it behaves in the same way as map
function. In another words, it transform a sequence of items to another sequence, by applying function f
to every item in the original collection. The function f
takes index as a first parameter and item as second:
(map-indexed
(fn [index item] [index item])
'(:a :b :c :d :e :f))
=> ([0 :a] [1 :b] [2 :c] [3 :d] [4 :e] [5 :f])
This way we don’t need to use some kind of loop when we need to transform items in collection and we need to know the position of item in the sequence.
(side note: all those function has also version that produce transducer, but for simplicity I’ve skipped it)