<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Harsh Srivastava</title>
    <description>The latest articles on DEV Community by Harsh Srivastava (@hksrivastava).</description>
    <link>https://dev.to/hksrivastava</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F929860%2Fc2160d30-a4e0-41e1-9e73-4b3c07228df9.jpg</url>
      <title>DEV Community: Harsh Srivastava</title>
      <link>https://dev.to/hksrivastava</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/hksrivastava"/>
    <language>en</language>
    <item>
      <title>3 Ways to Convert Numbers to Words in Clojure: A Performance Comparison</title>
      <dc:creator>Harsh Srivastava</dc:creator>
      <pubDate>Wed, 11 Jan 2023 19:56:18 +0000</pubDate>
      <link>https://dev.to/hksrivastava/3-ways-to-convert-numbers-to-english-in-clojure-a-performance-comparison-2eb7</link>
      <guid>https://dev.to/hksrivastava/3-ways-to-convert-numbers-to-english-in-clojure-a-performance-comparison-2eb7</guid>
      <description>&lt;p&gt;Hey, dev.to community!&lt;/p&gt;

&lt;p&gt;Today I want to share three different ways of converting a number (in the range of [0, 999,999,999,999]) to its English word form. For example, 34567 would become "thirty-four thousand five hundred sixty-seven" and 1001001 would become "one million one thousand one".&lt;/p&gt;

&lt;h2&gt;
  
  
  First Implementation: &lt;code&gt;num-to-word-1&lt;/code&gt;
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;(:require [clojure.pprint :refer [cl-format]]
            [clojure.string :as str])

(defn num-to-word-1 [n]
  (if (&amp;lt;= 0 n 999999999999)
    (str/replace
     (cl-format nil "~R" n)
     "," "")
    (throw (IllegalArgumentException.))))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It uses a built-in function &lt;code&gt;cl-format&lt;/code&gt; which is a part of &lt;code&gt;clojure.pprint&lt;/code&gt; library. Although it is the most readable and easy to implement (out of the three implementations discussed in this article), it is also the slowest. &lt;/p&gt;

&lt;h2&gt;
  
  
  Second Implementation: &lt;code&gt;num-to-word-2&lt;/code&gt;
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;(def ^:private ones {\0 "" \1 "one", \2 "two", \3 "three", \4 "four", \5 "five", \6 "six",
           \7 "seven", \8 "eight", \9 "nine"})

(def ^:private tens {\0 "",
           \1 {\0 "ten" \1 "eleven", \2 "twelve", \3 "thirteen", \4 "fourteen",
               \5 "fifteen", \6 "sixteen", \7 "seventeen", \8 "eighteen", \9 "nineteen"},
           \2 "twenty", \3 "thirty", \4 "forty", \5 "fifty",
           \6 "sixty", \7 "seventy", \8 "eighty", \9 "ninety"})

(def ^:private hundreds {\0 "" \1 "one hundred", \2 "two hundred", \3 "three hundred", \4 "four hundred",
               \5 "five hundred", \6 "six hundred", \7 "seven hundred", \8 "eight hundred", \9 "nine hundred"})

(defn- ones-tens [o t]
  (if (= \1 t)
    [((tens \1) o)]
    (-&amp;gt;&amp;gt; [(tens t) (ones o)]
         (remove #(or (nil? %) (empty? %)))
         (interpose "-")
         (apply str)
         vector)))

(defn- parse-item [item]
  (let [label (keys item)
        [o t h] (first (vals item))]
    (if (= \0 o t h)
      '()
      (concat label (ones-tens o t) [(hundreds h)]))))

(defn num-to-word-2 [n]
  (cond
    (= 0 n) "zero"
    (&amp;lt;= 1 n 999999999999)
    (let [scale ["" "thousand" "million" "billion"]
          nil-empty? #(or (nil? %) (empty? %))]
      (-&amp;gt;&amp;gt; n str reverse
           (partition-all 3)
           (map hash-map scale)
           (reduce #(into %1 (parse-item %2)) '())
           (remove nil-empty?)
           (interpose " ")
           (apply str)))
    :else (throw (IllegalArgumentException. "Number out of range"))))

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This implementation uses three separate maps of &lt;code&gt;ones&lt;/code&gt;, &lt;code&gt;tens&lt;/code&gt;, and &lt;code&gt;hundreds&lt;/code&gt;. The idea is to a number &lt;code&gt;n&lt;/code&gt;, convert it into a string, then break it into groups of three &lt;code&gt;(partition-all 3)&lt;/code&gt;, attach a scale to each group &lt;code&gt;(map hash-map scale)&lt;/code&gt;, iterate over the groups using &lt;code&gt;reduce&lt;/code&gt; converting each group into words using helper functions &lt;code&gt;parse-item&lt;/code&gt; and &lt;code&gt;ones-tens&lt;/code&gt;, then finally combine and convert the result back into a string to form the final output.&lt;/p&gt;

&lt;h2&gt;
  
  
  Third Implementation: &lt;code&gt;num-to-word-3&lt;/code&gt;
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;(:require [clojure.string :as str])

(def ^:private lookup
  {0 "zero" 1 "one" 2 "two" 3 "three" 4 "four" 
   5 "five" 6 "six" 7 "seven" 8 "eight" 9 "nine" 

   10 "ten" 11 "eleven" 12 "twelve" 13 "thirteen" 14 "fourteen"
   15 "fifteen" 16 "sixteen" 17 "seventeen" 18 "eighteen" 
   19 "nineteen"

   20 "twenty" 30 "thirty" 40 "forty"50 "fifty" 
   60 "sixty" 70 "seventy" 80 "eighty" 90 "ninety"})

(def ^:private scale
  [[1000000000 "billion"]
   [1000000    "million"]
   [1000       "thousand"]
   [100        "hundred"]])

(defn- to-word [n]
  (cond (contains? lookup n) (lookup n)
        (&amp;lt; n 100) (let [r (rem n 10)]
                    (str (to-word (- n r)) "-" (to-word r)))
        :else
        (let [[[limit label]] (drop-while (fn [[l]] (&amp;lt; n l)) scale)
              [qs r] ((juxt (comp to-word quot) rem) n limit)
              rs (when-not (zero? r) (to-word r))]
          (str/trimr (str/join " " [qs label rs])))))

(defn num-to-word-3 [n]
  (if (not (&amp;lt;= 0 n 999999999999))
    (throw (IllegalArgumentException.))
    (to-word n)))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This implementation uses a single &lt;code&gt;lookup&lt;/code&gt; map for the numbers 0-19 and {20 30 40 50 60 70 80 90} and a 2D vector &lt;code&gt;scale&lt;/code&gt;. The function makes use of recursion. If the number is in &lt;code&gt;lookup&lt;/code&gt;, the corresponding string is returned or else the function matches the number against &lt;code&gt;scale&lt;/code&gt; to determine a divisor, use this divisor to extract digits (&lt;code&gt;quot&lt;/code&gt; and &lt;code&gt;rem&lt;/code&gt;), and recalls itself for the extracted digits.&lt;/p&gt;

&lt;h2&gt;
  
  
  Comparison
&lt;/h2&gt;

&lt;p&gt;Given below is the time taken by each of the three functions to convert 100,000 random numbers into words.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;(time (dotimes [_ 100000]
        (num-to-word-1 (rand-int 1000000000))))
;; "Elapsed time: 1448.9357 msecs"
;; nil

(time (dotimes [_ 100000]
        (num-to-word-2 (rand-int 1000000000))))
;; "Elapsed time: 889.3392 msecs"
;; nil

(time (dotimes [_ 100000]
        (num-to-word-3 (rand-int 1000000000))))
;; "Elapsed time: 374.0896 msecs"
;; nil
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Keep exploring, experimenting and testing different options. Happy coding!&lt;/p&gt;

</description>
      <category>python</category>
      <category>developer</category>
    </item>
  </channel>
</rss>
