3
votes

Let's make frequency map:

(reduce #(update-in %1 [%2] (fnil inc 0)) {} ["a" "b" "a" "c" "c" "a"])

My concern is expression inside lambda #(...) - is it the canonical way to do it? Can I do it better/shorter?

EDIT: Another way I found:

(reduce #(assoc %1 %2 (inc %1 %2 0)) {} ["a" "b" "a" "c" "c" "a"])

Seems like very similar, what are pros/cons? Performance?

3
how is your second example suppose to work actually? you will recieve clojure.lang.ArityException because of passing 3 args to (inc). - user4813927

3 Answers

9
votes

Since Clojure 1.2, there is a frequencies function in clojure.core:

user=> (doc frequencies)
-------------------------
clojure.core/frequencies
([coll])
  Returns a map from distinct items in coll to the number of times
  they appear.

Example:

user=> (frequencies ["a" "b" "a" "c" "c" "a"])
{"a" 3, "b" 1, "c" 2}

It happens to use transients and ternary get; see (source frequencies) for the code, which is as idiomatic as it gets while being highly performance-aware.

1
votes

There is no need to use update-in. My way would be:

(defn frequencies [coll]
  (reduce (fn [m e]
              (assoc m e (inc (m e 0))))
          {} coll))

Update: I assumed you knew frequencies was in core also and this was just an exercise.

I did a guest lecture a while ago in which I explained how you can get to this solution step by step. Won't be much new for you, since you were already close to the core solution, but maybe it is of value to someone else reading this question. The slides are in Dutch. If you change .html to .org it's easier to get the source code:

http://michielborkent.nl/gastcollege-han-20-06-2013/gastcollege.html http://michielborkent.nl/gastcollege-han-20-06-2013/gastcollege.org

0
votes

Another approach using only 'assoc' and recursion:

(defn my-frequencies-helper [freqs a-seq]
  (if (empty? a-seq)
    freqs
    (let [fst (first a-seq)
          new_set (if (contains? freqs fst)
                    (assoc freqs fst (inc (get freqs fst)))
                    (assoc freqs fst 1))]
      (my-frequencies-helper new_set (rest a-seq)))))

(defn my-frequencies [a-seq]
  (my-frequencies-helper {} a-seq))

(my-frequencies [1 1 2 2 :D :D :D])

=> {1 2, 2 2, :D 3}

(my-frequencies [:a "moi" :a "moi" "moi" :a 1])

=> {:a 3, "moi" 3, 1 1}