DEV Community


Discussion on: Daily Challenge #67- Phone Directory

brightone profile image
Oleksii Filonenko

I'm still a newbie with Clojure, but I already kind of enjoy it.

Disclaimer: may not be "The Clojure Way", I don't know.

(ns day-67-phone-directory
  (:require [clojure.string :refer [replace split-lines trim]]))

(def regexps
  "A map of record fields to regular expressions to match them.
  The first capture group is the meaningful part to extract."
  {:phone #"\+(\d+-\d+-\d+-\d+)" :name #"<([^>]+?)>"})

(defn- find-first-group
  "Find `regex` in `book`, then extract the first capture group."
  [book regex]
  (-> regex (re-find book) (get 1)))

(defn- remove-regex
  "Removes the match of `regex` from `book`."
  [book regex]
  (replace book regex ""))

(defn- clean
  "Cleans an `address` into a kinda-human-readable state."
  (-> address
      (replace #"_" " ")
      (replace #"[^a-zA-Z0-9 .]" "")

(defn- line->record
  "Parse a `line` to a book record.
  `:address` is the leftover part after extracting phone and name."
  (let [[record address]
        (->> regexps keys
            (reduce (fn [[record rest] key]
                      [(assoc record key
                              (find-first-group rest (regexps key)))
                       (remove-regex rest (regexps key))])
                    [{} line]))]
    (assoc record :address (clean address))))

(defn phone
  "Returns information about `number` in `book`, or an error message.
  Result depends on how many people with the same `number` were found."
  [book number]
  (let [matching (->> book split-lines (map line->record)
                     (filter #(->> % :phone (= number))))]
    (case (count matching)
      0 (str "Error => Not found: " number)
      1 (let [record (first matching)]
          (format "Phone => %s, Name => %s, Address => %s"
                  (:phone record) (:name record) (:address record)))
      (str "Error => Too many people: " number))))