August 25, 2018

Just Juxt #11: Partition a Sequence (4clojure #54)

Partitions

Write a function which returns a sequence of lists of x items each. Lists of less than x items should not be returned.

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

(deftest partition-seq-test
  (is (= (partition-seq 3 (range 9)) '((0 1 2) (3 4 5) (6 7 8))))
  (is (= (partition-seq 2 (range 8)) '((0 1) (2 3) (4 5) (6 7))))
  (is (= (partition-seq 3 (range 8)) '((0 1 2) (3 4 5)))))

(run-tests)

This problem is holding up a big sign that says:

"Hey! The partition function is totally awesome, you should check out how it works!"

So here's how it works:

user=> (source partition)
(defn partition                                
"Returns a lazy sequence of lists of n items each, at offsets step
apart. If step is not supplied, defaults to n, i.e. the partitions
do not overlap. If a pad collection is supplied, use its elements as
necessary to complete last partition upto n items. In case there are
not enough padding elements, return a partition with less than n items."
{:added "1.0"
 :static true}
([n coll]
  (partition n n coll))
([n step coll]
 (lazy-seq
   (when-let [s (seq coll)]
     (let [p (doall (take n s))]
       (when (= n (count p))
         (cons p (partition n step (nthrest s step))))))))
([n step pad coll]
 (lazy-seq
   (when-let [s (seq coll)]
     (let [p (doall (take n s))]
       (if (= n (count p))
         (cons p (partition n step pad (nthrest s step)))
         (list (take n (concat p pad)))))))))

Learning from these concepts, we could create a solution like this:

(defn partition-seq [n coll]
  (loop [c coll partitioned []]
    (if (< (count c) n)
        partitioned
        (recur (drop n c) (conj partitioned (take n c))))))
  
(run-tests)

But here's a way to do it with juxt:

(defn partition-seq [n coll]
  (let [[a b] ((juxt take drop) n coll)]
    (if (= (count a) n)
      (cons a (partition-seq n b)))))

(run-tests)

Inside our let binding is a destructuring form, which pulls apart the items (plugging in the first test case) and creates local bindings for them named a and b:

((juxt take drop) 3 (range 9))
Tags: coding exercises KLIPSE 4clojure Cryogen juxt