Counting occurences

unsorted — cgrand, 27 April 2009 @ 17 h 44 min

One asked me how to count occurrences of each value in a collection. I answered (reduce #(assoc %1 %2 (inc (%1 %2 0))) {} coll). Since it can take some time to get accustomed to the functional way of thought, here is how one can work such an expression out:

How to count all occurrences of 42?
(count (filter #{42} coll))
How to express count using reduce?
(defn my-count [coll] (reduce (fn [n _] (inc n)) 0 coll))
How to count all occurrences of 42 using reduce?
(reduce (fn [n _] (inc n)) 0 (filter #{42} coll))
Can you get rid of the filter?
(reduce (fn [n x] (if (= 42 x) (inc n) n)) 0 coll)
I’d like the result to be {42 occurences-count}.
(reduce (fn [m x] (if (= 42 x) (assoc m 42 (inc (m 42))) m)) {42 0} coll)
42 is hardcoded in four places, it’s bad!
(reduce (fn [m x] (if (= 42 x) (assoc m x (inc (m x))) m)) {42 0} coll)
Can’t you replace {42 0} with {}?
No (inc (m x)) would fail because (m x) would return nil.
How does one provide a default value when the key is not found ?
(a-map a-key default-value)
Can’t you replace {42 0} with {}?
(reduce (fn [m x] (if (= 42 x) (assoc m x (inc (m x 0))) m)) {} coll)
I changed my mind I’d like you to count occurrences of each value.
Easy! (reduce (fn [m x] (assoc m x (inc (m x 0)))) {} coll) or, terser, (reduce #(assoc %1 %2 (inc (%1 %2 0))) {} coll)

Exercise:

What does (merge-with + {:a 12} {:b 4} {:a 3 :b 7}) return?
Can you count occurrences of each value in a collection using merge-with?

3 Comments »

  1. I like your explanation. Couldn’t be clearer.
    But it seems that clojure.core now has a (frequencies coll) function that does this.

    Comment by Alexandre Jasmin — 4 July 2011 @ 20 h 19 min
  2. My thought process on this would probably be something like:

    What does the final result need to look like?
    {:a 5, :b 1, :c 4}

    Can I build that up incrementally (as opposed to multiple passes)?
    Yes, given {:a 5, :b 1, :c 4} and another occurrence of :b, I can produce {:a 5, :b 2, :c 4}. Good, so no post-processing or multi-pass processing is needed.

    Is there a good initial condition?
    {}

    etc.

    Comment by Tim McCormack — 21 November 2011 @ 2 h 42 min
  3. Very interesting blogs – I am also reading your excellent book (when’s the next edition?)

    Counting occurrences using merge-with:

    (reduce #(merge-with + %1 {%2 1}) {} coll)

    Comment by JF Rompre — 5 August 2013 @ 2 h 46 min

RSS feed for comments on this post. TrackBack URI

Leave a comment

(c) 2014 Clojure and me | powered by WordPress with Barecity