August 26, 2018

Just Juxt #12: Count Occurrences (4clojure #55)

Frequencies

Write a function which returns a map containing the number of occurrences of each distinct item in a sequence.

...but without using frequencies!

(ns live.test
  (:require [cljs.test :refer-macros [deftest is testing run-tests]]))
  
(defn freqs [s]
  )

(deftest freqs-test
  (is (= (freqs [1 1 2 3 2 1 1]) {1 4, 2 2, 3 1}))
  (is (= (freqs [:b :a :b :a :b]) {:a 2, :b 3}))
  (is (= (freqs '([1 2] [1 3] [1 3])) {[1 2] 1, [1 3] 2})))

(run-tests)

Here is the source for the frequencies function:

user=> (source frequencies)
(defn frequencies
"Returns a map from distinct items in coll
to the number of times they appear."
{:added "1.2"
:static true}
[coll]
(persistent!
  (reduce (fn [counts x]
         (assoc! counts x (inc (get counts x 0))))
       (transient {}) coll)))

Here's the juxtification:

(defn freqs [s]
  (into {}
        (map (juxt first count)
             (partition-by identity (sort s)))))
  
(run-tests)

This is a pattern that we haven't yet seen - using juxt to return a function and maping it.

Let's break this down:

(sort [1 1 2 3 2 1 1])
(partition-by identity '(1 1 1 1 2 2 3))

Now for the fun part, the 2 functions passed to juxt. However, we're not calling it on the collection this time - instead we're maping it:

(first '(1 1 1 1))
(count '(1 1 1 1))
(first '(2 2))
(count '(2 2))
(first '(3))
(count '(3))

Which gives us:

(map (juxt first count)
     '((1 1 1 1) (2 2) (3)))

Now all that's left to do is throw it into a map!

(into {} '([1 4] [2 2] [3 1]))

I hope you enjoyed today's juxt, and possibly learned something. I know I sure did ;)

Tags: coding exercises KLIPSE 4clojure Cryogen juxt