Skip to content

Instantly share code, notes, and snippets.

@ericnormand
Created March 8, 2021 20:54
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/d744381a3a625af105245fe5f9ecc942 to your computer and use it in GitHub Desktop.
Save ericnormand/d744381a3a625af105245fe5f9ecc942 to your computer and use it in GitHub Desktop.
417 PurelyFunctional.tv 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.

Examples

(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.

@daniel-quintoandar
Copy link

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

@diavoletto76
Copy link

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

@arthurulacerda
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
  [st]
  (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))))

@alex-gerdom
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)
                      (reverse)
                      ;; Escape user input
                      (map clojure.string/re-quote-replacement)
                      ;; Join it to form alternating regex "p1|p2|...|pn"
                      (clojure.string/join "|")
                      (re-pattern))
         replacements (hash-map p1 p2, p2 p1)]
     (clojure.string/replace s pattern replacements)))

@litemerafrukt
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'");

@steffan-westcott
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 "[[+++((" "[[" "((")
=> "((+++[["

@jrwdunham
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.

@tvirolai
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))))

@damesek
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 [])
           input)
         persistent!
         (apply str))))

with xform (works, but could be better)

(defn -swap-xform [char1 char2]
  (fn [xf]
    (fn
      ([] (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")

@just-sultanov
Copy link

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

@nwjsmith
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)))

@vmpj
Copy link

vmpj commented Mar 11, 2021

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

@filippocostalli
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)))

@tylerw
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}))

@burnall
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.

@prairie-guy
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}))

@sim-f
Copy link

sim-f commented Apr 11, 2021

Only works on replacements strings with length 1:

(defn swap [string rstr1 rstr2]
  (->> string
       (reduce
        (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