Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Clojure: idiomatic code for frequency map

Tags:

clojure

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?

like image 503
demi Avatar asked Mar 04 '26 03:03

demi


2 Answers

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.

like image 50
Michał Marczyk Avatar answered Mar 05 '26 17:03

Michał Marczyk


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

like image 23
Michiel Borkent Avatar answered Mar 05 '26 16:03

Michiel Borkent