Skip to main content
update
Source Link
(deftest test-records
  (testing "edge cases"
    (is (= (breaking-records '()[]) {:nbetter 0 :nworse 0}) "no games played yet")
    (is (= (breaking-records '(5)[5]) {:nbetter 0 :nworse 0}) "single game"))
  (testing "hackerrank examples"
    (is (= (breaking-records '(10[10 5 20 20 4 5 2 25 1)1]) {:nbetter 2 :nworse 4}))
    (is (= (breaking-records '(3[3 4 21 36 10 28 35 5 24 42)42]) {:nbetter 4 :nworse 0}))))

; ***** NOTE: it's much easier to use vectors like [1 2 3] instead of a quoted list `(1 2 3)

Please see this list of documentation, esp. the Clojure CheatSheet. Also, the template project as a whole shows how I like to structure things. :)

The function that helps the most is partition. See the docs.


Slight refactoring

You can simplify it a small amount and make it a bit more compact by using more specialized functions like reduce and cond->. This version uses a map to hold state and reduce to perform the looping:

(defn breaking-records
  [scores]
  (let [state-init     {:low     (first scores)
                        :high    (first scores)
                        :nworse  0
                        :nbetter 0}
        accum-stats-fn (fn [state score-pair]
                         ; Use map destructuring to pull out the 4 state variables
                         (let [{:keys [low high nworse nbetter]} state 
                               new-score (second score-pair)
                               state-new {:low     (min new-score low)
                                          :high    (max new-score high)
                                          :nworse  (cond-> nworse
                                                     (< new-score low) (inc))
                                          :nbetter (cond-> nbetter
                                                     (< high new-score) (inc))}]
                           state-new))
        state-final    (reduce accum-stats-fn
                         state-init
                         (partition 2 1 scores))
        result         (select-keys state-final [:nworse :nbetter])]
    result))
(deftest test-records
  (testing "edge cases"
    (is (= (breaking-records '()) {:nbetter 0 :nworse 0}) "no games played yet")
    (is (= (breaking-records '(5)) {:nbetter 0 :nworse 0}) "single game"))
  (testing "hackerrank examples"
    (is (= (breaking-records '(10 5 20 20 4 5 2 25 1)) {:nbetter 2 :nworse 4}))
    (is (= (breaking-records '(3 4 21 36 10 28 35 5 24 42)) {:nbetter 4 :nworse 0})))
  )
(deftest test-records
  (testing "edge cases"
    (is (= (breaking-records []) {:nbetter 0 :nworse 0}) "no games played yet")
    (is (= (breaking-records [5]) {:nbetter 0 :nworse 0}) "single game"))
  (testing "hackerrank examples"
    (is (= (breaking-records [10 5 20 20 4 5 2 25 1]) {:nbetter 2 :nworse 4}))
    (is (= (breaking-records [3 4 21 36 10 28 35 5 24 42]) {:nbetter 4 :nworse 0}))))

; ***** NOTE: it's much easier to use vectors like [1 2 3] instead of a quoted list `(1 2 3)

Please see this list of documentation, esp. the Clojure CheatSheet. Also, the template project as a whole shows how I like to structure things. :)

The function that helps the most is partition. See the docs.


Slight refactoring

You can simplify it a small amount and make it a bit more compact by using more specialized functions like reduce and cond->. This version uses a map to hold state and reduce to perform the looping:

(defn breaking-records
  [scores]
  (let [state-init     {:low     (first scores)
                        :high    (first scores)
                        :nworse  0
                        :nbetter 0}
        accum-stats-fn (fn [state score-pair]
                         ; Use map destructuring to pull out the 4 state variables
                         (let [{:keys [low high nworse nbetter]} state 
                               new-score (second score-pair)
                               state-new {:low     (min new-score low)
                                          :high    (max new-score high)
                                          :nworse  (cond-> nworse
                                                     (< new-score low) (inc))
                                          :nbetter (cond-> nbetter
                                                     (< high new-score) (inc))}]
                           state-new))
        state-final    (reduce accum-stats-fn
                         state-init
                         (partition 2 1 scores))
        result         (select-keys state-final [:nworse :nbetter])]
    result))
Source Link

I re-wrote your solution to use more typical Clojure features. When you are looping over data and need to keep track of accumulated state, it is hard to beat loop/recur. A first example:

(ns tst.demo.core
  (:use clojure.test))

(defn breaking-records
  [scores]
  ; this loop has 5 variables. Init all of them
  (loop [low         (first scores)
         high        (first scores)
         nworse      0
         nbetter     0
         score-pairs (partition 2 1 scores)]
    (if (empty? score-pairs)
      {:nbetter nbetter :nworse nworse}
      (let [curr-score-pair (first score-pairs)
            new-score       (second curr-score-pair)]
        ; start the next iteration with modified versions of the 5 loop vars
        (recur
          (min new-score low)
          (max new-score high)
          (if (< new-score low)
            (inc nworse)
            nworse)
          (if (< high new-score)
            (inc nbetter)
            nbetter)
          (rest score-pairs))))))

and unit tests:

(deftest test-records
  (testing "edge cases"
    (is (= (breaking-records '()) {:nbetter 0 :nworse 0}) "no games played yet")
    (is (= (breaking-records '(5)) {:nbetter 0 :nworse 0}) "single game"))
  (testing "hackerrank examples"
    (is (= (breaking-records '(10 5 20 20 4 5 2 25 1)) {:nbetter 2 :nworse 4}))
    (is (= (breaking-records '(3 4 21 36 10 28 35 5 24 42)) {:nbetter 4 :nworse 0})))
  )