Skip to content

Instantly share code, notes, and snippets.

@ericnormand
Last active February 26, 2021 14:49
Show Gist options
  • Save ericnormand/23195520ccd38d391d6cbcd907c0ab81 to your computer and use it in GitHub Desktop.
Save ericnormand/23195520ccd38d391d6cbcd907c0ab81 to your computer and use it in GitHub Desktop.
412 PurelyFunctional.tv Newsletter

Valid names

This challenge looks like a fun experiment in building a simple rules-based validator.

Definitions:

  1. A name is a sequence of terms separated by a space. It must have at least 2 terms. The last term must be a word.
  2. A term is either an initial or a word.
  3. An initial is a single capital letter followed by a period.
  4. A word is a capital letter followed by one or more letters (upper or lower case).

Write a function that checks whether a string is a valid name.

Examples

Valid names:

  • George R. R. Martin
  • Abraham Lincoln
  • J. R. Bob Dobbs
  • H. G. Wells

Invalid names:

  • J R Tolkien (no periods)
  • J. F. K. (must end in word)
  • Franklin (must have at least two terms)

Thanks to this site for the challenge idea where it is considered Expert in JavaScript. The problem has been modified from the original.

Please submit your solutions as comments on this gist.

@andyfry01
Copy link

@sztamas Nice to see another usage of spec! This was my first time using the library, really interesting stuff 👍

(ns spec-names
  (:require [clojure.spec.alpha :as s]
            [clojure.string :as str]))

; input values
(def validnames ["Abraham Lincoln"
                 "George R. R. Martin"
                 "J. R. Bob Dobbs"
                 "H. G. Wells"])

(def invalidnames ["J R Tolkien"
                   "J. F. K."
                   "Franklin"])
; predicates
(defn isCapitalized? [string] (= string (str/capitalize string)))
(defn lenGt2? [string] (> (count string) 2))
(defn endsWithPeriod? [string] (= "." (str (last string))))

; specs
(s/def ::valid-name (s/and isCapitalized? lenGt2?))
(s/def ::valid-initial (s/and isCapitalized? endsWithPeriod?))
(s/def ::nameorinitial (s/or :name ::valid-name :initial ::valid-initial))

; validator
(defn name-validator [name]
  (let [splitname (str/split name #" ")
        lastname (last splitname)
        init-terms (drop-last splitname)]

    (every? true?
            [(s/valid? (s/+ ::nameorinitial) init-terms)
             (s/valid? ::valid-name lastname)])))

; run tests!
(mapv name-validator invalidnames)
(mapv name-validator validnames)

@sztamas
Copy link

sztamas commented Feb 14, 2021

@andyfry01 Yes, I thought spec can be used nicely for this kind of problem!

@pmonks
Copy link

pmonks commented Feb 23, 2021

@pieterbreed you're too kind; in the real world I would absolutely use a solution closer to yours than the one I posted. Regexes are awesome! 😉

@galuque
Copy link

galuque commented Feb 26, 2021

Spec really is shining in this one wow

(ns pftv.challenges.412
  (:require [clojure.spec.alpha :as spec]
            [clojure.string :as str]))

(spec/def ::initial (partial re-matches #"[A-Z]\."))

(spec/def ::word (partial re-matches #"[A-Z][A-Za-z]+"))

(spec/def ::term (spec/or :initial ::initial :word ::word))

(spec/def ::name (spec/cat
                  :terms (spec/+ ::term)
                  :ends-in-word ::word))

(defn valid-name? [name]
  (let [name-vec (str/split name #" ")]
    (spec/valid? ::name name-vec)))

(def valid-names ["George R. R. Martin"
                  "Abraham Lincoln"
                   "J. R. Bob Dobbs"
                  "H. G. Wells"])

(def invalid-names ["J R Tolkien"
                      "J. F. K."
                      "Franklin"])
(every? true?
        (map valid-name? valid-names))
;; => true

(every? false?
        (map valid-name? invalid-names))
;; => true

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment