# Clojure: map-occurrence

Sometimes you want behaviour that differs based on the number of times an item has been seen in a sequence. Clojure doesn't come with a function that does this. Before you say "What about `frequencies`?", `frequencies` gives you the total number of occurrences in a sequence of items, not the occurrence count.

Say we have the following sequence:

``````[:chest :dirt :chest :chest :dirt :chest :dirt]
``````

We want to do different transformations based on which occurrence of the `:chest` keyword we are mapping over. For example: transforming the first occurrence into `:diamond` and the second into `:ruby` etc.

``````[:diamond :dirt :ruby :gold :dirt :grail :dirt]
``````

Let's consider `map-indexed` for a minute.

``````(map-indexed (fn [i x] (if (= i 3) )) [7 8 9 10 4 9])

=>
(7 8 9 100 4 9)
``````

It maps over a sequence passing the current index of item and the item into a function. In the example above we want to square the item at index 3 in the sequence.

We want to make a similar function except instead of the index we want to pass the occurrence of the item.

We can write a function with reduce that achieves this:

``````(defn map-occurrence [f s]
(:result
(reduce (fn [{:keys [result x-count] :as acc} x]
(let [x-count (assoc x-count x (inc (get x-count x 0)))]
(-> (update acc :result conj (f (x-count x) x))
(assoc :x-count x-count))))
{:result [] :x-count {}}
s)))
``````

Personally, I find the `reduce` implementation quite dense and prefer a recursive solution.

``````(defn map-occurrence
([f s] (map-occurrence f s {}))
([f s x-count]
(lazy-seq
(when-let [[x & xs] (seq s)]
(let [x-count (assoc x-count x (inc (get x-count x 0)))]
(cons (f (x-count x) x)
(map-occurrence f xs x-count)))))))
``````

We use `lazy-seq` to define a function that recursively builds a list of items that are the result of `(f (x-count x) x)` where `x-count` is the current count of `x`. The use of `(when-let [[x & xs] (seq s)] ...)` is a common pattern when building lazy sequences, allowing you to apply a function to the head of the sequence and then call it recursively on the tail.

``````(map-occurrence
(fn [occ item]
(if (= item :chest)
({1 :diamond 2 :ruby 3 :gold 4 :grail} occ)
item))
[:chest :dirt :chest :chest :dirt :chest :dirt])

=>
(:diamond :dirt :ruby :gold :dirt :grail :dirt)
``````

Honestly, the need for `map-occurrence` doesn't come up often but when it does it can be particularly useful tool.