Skip to content

Instantly share code, notes, and snippets.

@ericnormand
Created March 8, 2021 20:54
Show Gist options
  • Select an option

  • Save ericnormand/d744381a3a625af105245fe5f9ecc942 to your computer and use it in GitHub Desktop.

Select an option

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
Copy Markdown
(defn swap [st a b]
  (transduce
   (map (fn [c]
          (condp = c
            (first a) b
            (first b) a
            c)))
   str
   st))

@diavoletto76

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

@arthurulacerda

arthurulacerda commented Mar 8, 2021

Copy link
Copy Markdown
(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
Copy Markdown
(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
Copy Markdown
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

steffan-westcott commented Mar 8, 2021

Copy link
Copy Markdown
(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

jrwdunham commented Mar 9, 2021

Copy link
Copy Markdown

(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

tvirolai commented Mar 9, 2021

Copy link
Copy Markdown
(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

damesek commented Mar 9, 2021

Copy link
Copy Markdown

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
Copy Markdown
(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

nwjsmith commented Mar 9, 2021

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

@vmpj

vmpj commented Mar 11, 2021

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

@filippocostalli

filippocostalli commented Mar 12, 2021

Copy link
Copy Markdown
;; 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

tylerw commented Mar 13, 2021

Copy link
Copy Markdown
; 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

burnall commented Mar 14, 2021

Copy link
Copy Markdown
(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

prairie-guy commented Apr 5, 2021

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

@sim-f

sim-f commented Apr 11, 2021

Copy link
Copy Markdown

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