Skip to content

Instantly share code, notes, and snippets.

@ericnormand
Created January 4, 2021 14:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ericnormand/18a6858ea77c0a306bc75e4b3d7c5ce6 to your computer and use it in GitHub Desktop.
Save ericnormand/18a6858ea77c0a306bc75e4b3d7c5ce6 to your computer and use it in GitHub Desktop.
409 PurelyFunctional.tv Newsletter

First before second

Write a function that takes a string and two letters. The function should return true if every instance of the first letter comes before every instance of the second letter.

Examples

(first-before? "A rabbit jumps joyfully" \a \j) ;=> true
(first-before? "A jolly rabbit jumps joyfully" \a \j) ;=> false
(first-before? "Don't tread on me" \t \m) ;=> true
(first-before? "Every React podcast" \t \d) ;=> false

Notes

  • Letters will be mixed case. You should treat \A the same as \a.
  • You can assume the strings will contain instances of both letters.

Thanks to this site for the challenge idea where it is considered Hard in Python.

Please submit your solutions as comments on this gist.

@Toni-zgz
Copy link

Toni-zgz commented Jan 5, 2021

(ns first-before)

(require '[clojure.test :as test])
(require '[clojure.string :as str])

(defn first-before? [cadena caracter-1 caracter-2]
(let [cadena-minusculas (str/lower-case cadena)
ultimo-indice-caracter-1 (str/last-index-of cadena-minusculas caracter-1)
primer-indice-caracter-2 (str/index-of cadena-minusculas caracter-2)]
(if (< ultimo-indice-caracter-1 primer-indice-caracter-2)
true
false)))

(test/deftest first-before-test
(test/is (= true (first-before? "A rabbit jumps joyfully" \a \j)))
(test/is (= false (first-before? "A jolly rabbit jumps joyfully" \a \j)))
(test/is (= true (first-before? "Don't tread on me" \t \m)))
(test/is (= false (first-before? "Every React podcast" \t \d))))

(test/run-tests 'first-before)

@mchampine
Copy link

Looks like mine is logically identical to ndonolli's solution, with a few different choices in implementation:

(defn first-before? [s c1 c2]
  (let [fs (filter (hash-set c1 c2) (clojure.string/lower-case s))
        [fc sc :as mfp] (map first (partition-by identity fs))]
    (and (= fc c1) (= sc c2) (= 2 (count mfp)))))

@mchampine
Copy link

mchampine commented Jan 5, 2021

@steffan-westcott - I'm not great w/ regexes, but doesn't this simplified pattern also work?

(str "(?i)" ch1 ".*" ch0)

Clever solution btw! I didn't think to match on patterns that violate the constraint. Nice.
Kudos also to @diavoletto76 for a concise solution.

@steffan-westcott
Copy link

@mchampine I found quoting (using \Q and \E) was needed to avoid issues where either character is significant to patterns, such as ] and {. For example, without quoting you can have errors like this:

=> (first-before? "[{" \[ \{)
PatternSyntaxException Illegal repetition near index 3
(?i){.*[
   ^  java.util.regex.Pattern.error (Pattern.java:1955)

For those unfamiliar, (?i) turns off case sensitivity for the remainder of the pattern.

@miner
Copy link

miner commented Jan 7, 2021

(require '[clojure.string :as str])

;; if c1 or c2 is not in s, return true (better than NPE)
(defn first-before? [s c1 c2]
  (let [s (str/lower-case s)
        ind2 (str/index-of s (str/lower-case c2))]
    (or (not ind2)
        (not (str/index-of s (str/lower-case c1) ind2)))))

@chopmo
Copy link

chopmo commented Jan 16, 2021

(defn first-before? [s c1 c2]
  (let [regex           (re-pattern (str c1 \| c2))
        matches         (re-seq regex (str/lower-case s))
        [group1 group2] (partition-by identity matches)]
    (and (= (str c1) (first group1))
         (= (str c2) (first group2)))))

@chopmo
Copy link

chopmo commented Jan 16, 2021

@diavoletto76 Your solution is my favorite by far! I didn't even consider leaning on those clojure.string functions. Only nitpick is that you seem to have forgotten to make it case insensitive :)

@diavoletto76
Copy link

@chopmo You are right. Thank you for pointing the case sensitivity issue out.

@luissantos
Copy link

(defn first-before?
  [s a b]
  (let [ lc clojure.string/lower-case
        a-pos (filter (complement nil?) (map-indexed #(if (= (lc a) (lc %2)) %1 nil) s))
        b-pos (filter (complement nil?) (map-indexed #(if (= (lc b) (lc %2)) %1 nil) s))
        all (concat a-pos b-pos)]
    (= all (sort all))))

@diavoletto76 your solution is great.

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