Wednesday, February 9, 2011

Partioning a Number Sequence in Clojure

I have the following input:

(def nums [123456789012 123456789012])

I'd like the following output:

[[1234 5678 9012] [1234 5678 9012]]

Note both of these sequence contain numbers not strings...

I figured this would be really simple by doing the following:

Convert each entry into a String
Partition each string by 4
Convert each partition back into an integer

Attempt 1:

(defn split-nums [nums factor]
       (fn [x] (Integer/valueOf (str x)))
       (partition factor (str %)))

(println (split-nums nums, 4))

When I run this I get the following error:

Caused by: java.lang.NumberFormatException: For input string: "clojure.lang.LazySeq@4834333c"
Which tells me I am dealing with a lazy sequence that I need to force evaluation on but when I try to (str (doall x)) I get the same result.

Attempt 2:

A version with / and mod. This also fixes leading zeros problem.

(defn int-partition [num size]
   (let [f (int (Math/pow 10 size))]
      (loop [n num l ()]
         (if (zero? n) 
            (vec l) 
            (recur (int (/ n f)) (conj l (mod n f)))))))

(defn split-nums [nums factor] (vec (map #(int-partition % factor) nums)))

Attempt 3:

(defn split-nums [nums factor]
  (map #(map (fn [x] (Integer/valueOf (apply str x))) ; apply str
             (partition factor (str %)))
(str (lazy-seq [1])) ; "clojure.lang.LazySeq@20"

(apply str (lazy-seq [1])) ; "1"

I'd probably write it to accept one number, then use map, instead of taking a coll.

(defn split-number [n factor]
  (->> (str n)
       (partition-all factor) ;; or partition
       (map (partial apply str))
       (map #(Integer/valueOf %))))
(map #(split-number % 4) [12345678 12345678]) ;; => ((1234 5678) (1234 5678))


No comments:

Post a Comment