Skip to content

Instantly share code, notes, and snippets.

Created March 8, 2021 20:54
Show Gist options
  • Save ericnormand/d744381a3a625af105245fe5f9ecc942 to your computer and use it in GitHub Desktop.
Save ericnormand/d744381a3a625af105245fe5f9ecc942 to your computer and use it in GitHub Desktop.
417 Newsletter

Character swap

Write a function that swaps every occurrence of one string with another, and vice versa. It is like clojure.string.replace but works both ways.


(swap "abc" "a" "b") ;=> "bac"
(swap "book" "k" "t") ;=> "boot"
(swap "Closure" "j" "s") ;=> "Clojure"
(swap "bee" "b" "e") ;=> "ebb"
(swap "abc" "1" "2") ;=> "abc"

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

Please submit your solutions as comments on this gist.

Copy link

(defn swap [st a b]
   (map (fn [c]
          (condp = c
            (first a) b
            (first b) a

Copy link

(defn swap [xs a b]
  (->> xs
       (map #(let [x' (str %)]
                  (cond (= x' a) b
                        (= x' b) a
                        :else x')))
       (clojure.string/join "")))

Copy link

arthurulacerda commented Mar 8, 2021

(swap "abc xyz" "abc" "xyz") ;=> "xyz abc"
(swap "abcdefg." "." "abc") ;=> ".defgabc"
(require '[clojure.string :as s])
(import 'java.util.regex.Pattern)
(defn str->pattern
  (re-pattern (Pattern/quote st)))
(defn swap
  [str-base str-x str-y]
  (s/join str-y (map #(s/join str-x (s/split % (str->pattern str-y) -1))
                     (s/split str-base (str->pattern str-x) -1))))

Copy link

(defn swap [s p1 p2]
   "Swap each occurence of p1 w/ p2 (and p2 with p1) in s"
   (let [pattern (->> [p1 p2]
                      ;; Longest strings should be first
                      (sort-by count)
                      ;; Escape user input
                      (map clojure.string/re-quote-replacement)
                      ;; Join it to form alternating regex "p1|p2|...|pn"
                      (clojure.string/join "|")
         replacements (hash-map p1 p2, p2 p1)]
     (clojure.string/replace s pattern replacements)))

Copy link

const swap = (s, a, b) => {
  let re = new RegExp(`(?:${a})|(?:${b})`, "g");

  return s.replace(re, match => (match === a ? b : a));
console.assert(swap("abc", "a", "b") === "bac", "should be 'bac'");
console.assert(swap("book", "k", "t") === "boot", "should be 'boot'");
console.assert(swap("Closure", "j", "s") === "Clojure", "should be 'Clojure'");
console.assert(swap("bee", "b", "e") === "ebb", "should be 'ebb'");
console.assert(swap("abc", "1", "2") === "abc", "should be 'abc'");
console.assert(swap("abc xyz", "abc", "xyz") === "xyz abc", "should be 'xyz abc'");

Copy link

steffan-westcott commented Mar 8, 2021

(defn swap [s x y]
  (clojure.string/replace s
                          (re-pattern (str "\\Q" x "\\E|\\Q" y "\\E"))
                          #(if (= % x) y x)))

Note that re-quote-replacement is used for replacement strings, so \Q .. \E is used instead to quote in the search pattern. The quoting is needed to deal with characters significant to regex syntax:

(swap "[[+++((" "[[" "((")
=> "((+++[["

Copy link

jrwdunham commented Mar 9, 2021

(defn swap [i [x] [y]] (->> i (map (fn [c] (get {x y y x} c c))) (apply str)))

The original question states "Write a function to replace all instances of character c1 with character c2 and vice versa" so it seems fair to assume args 2 and 3 are always 1-character strings. This fits with the inputs provided, though not with the letter of the challenge as stated above.

Copy link

tvirolai commented Mar 9, 2021

(defn swap [input char1 char2]
  (let [replacements {(first char1) (first char2)
                      (first char2) (first char1)}]
    (->> input
         (map (fn [c]
                (or (replacements c) c)))
         (apply str))))

Copy link

damesek commented Mar 9, 2021

Without (Stringbuilder.)

  (defn -swap [input char1 char2]
    (->> (reduce
           (fn [acc vv]
             (let [v (str vv)
                   item (or (and (= char1 v) char2)
                            (and (= char2 v) char1))]
               (conj! acc (or item v))))
           (transient [])
         (apply str))))

with xform (works, but could be better)

(defn -swap-xform [char1 char2]
  (fn [xf]
      ([] (xf))
      ([acc] (xf acc))
      ([acc v]
       (xf acc (or (or (and (= char1 (str v)) char2)
                       (and (= char2 (str v)) char1)) v))))))

(defn rf
  ([] (transient []))
  ([acc] (apply str (persistent! acc)))
  ([acc val] (conj! acc val)))

(transduce (-swap-xform "a" "b") rf "abc")

Copy link

(defn swap [s match replacement]
  (let [rules   {match       replacement
                 replacement match}
        replace (fn [x] (get rules x x))]
      (map (comp replace str)) str s)))

Copy link

nwjsmith commented Mar 9, 2021

(defn swap
  [s ch1 ch2]
  (->> s
       (map (fn [ch] (condp = (str ch) ch1 ch2 ch2 ch1 ch)))
       (apply str)))

Copy link

vmpj commented Mar 11, 2021

(defn swap [s r1 r2]
  (apply str (map #(get {r1 r2 r2 r1} (str %) %) s)))

Copy link

filippocostalli commented Mar 12, 2021

;; much better than my previous one and even faster
(defn swap [s ch1 ch2]
   (->> s
      (map #(cond
              (= (str %) ch1) ch2
              (= (str %) ch2) ch1
              :else %))
      (apply str)))

Copy link

tylerw commented Mar 13, 2021

; provided `a` and `b` are strings of length 1
(defn swap [s a b]
  (clojure.string/escape s {(first a) b (first b) a}))

Copy link

burnall commented Mar 14, 2021

(defn swap [s a b]
  (clojure.string/replace s 
                          (re-pattern (str a "|" b))
                          {a b, b a}))

Seems like most (if not all= previous solutions suggest length 1 for a and b, which is not in the original problem statement.

Copy link

prairie-guy commented Apr 5, 2021

(require '[clojure.string :as string])
(defn swap [s c1 c2]                                                                                                                                  
  (string/replace s (re-pattern (str c1 "|" c2)) {c1 c2, c2 c1}))

Copy link

sim-f commented Apr 11, 2021

Only works on replacements strings with length 1:

(defn swap [string rstr1 rstr2]
  (->> string
        (fn [acc c]
          (cond (= rstr1 (str c)) (conj acc rstr2)
                (= rstr2 (str c)) (conj acc rstr1)
                :else (conj acc c)))
       (apply str)))

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