Заменить определенное содержимое в строке, если существует в Clojure - PullRequest
0 голосов
/ 19 января 2020

Я уже искал сайт, но не нашел подходящего ответа на мой конкретный вопрос о замене подстрок. Я знаю, как заменить подстроку / регулярное выражение через clojure.string/replace, но не уверен, что использовать ее в этом случае.

Допустим, скажем. У меня есть несколько строк на первом месте:

(def test-str "I am a test string. :name to replace, :age to replace. And there are strange symbols in this string. And here are the keyword shall not modify, such as :not-interested.")
(def another-test-str ":nothing :to :replace")

И у меня есть таблица перевода:

(def translation-table {:name "Alice"
                :age  19})

Я хочу заменить :name в test-str на "Алиса", :age с 19, но не хотят заменять :not-interested. Таблица длинная, и разные строки (подлежащие замене) содержат разные ключевые слова.

Учитывая таблицу перевода, каков тогда возможный систематический c способ замены подстроки? А именно, я хочу функцию replace-with-translation:

(replace-with-translation test-str translation-table)
=> "I am a test string. Alice to replace, 19 to replace. And there are strange symbols in this string. And here are the keyword shall not modify, such as :not-interested."

(replace-with-translation another-test-str translation-table)
=> ":nothing :to :replace"

(replace-with-translation test-str {})
=> "I am a test string. :name to replace, :age to replace. And there are strange symbols in this string. And here are the keyword shall not modify, such as :not-interested."

Ответы [ 4 ]

6 голосов
/ 19 января 2020

string/replace может использовать функцию в качестве замены, а get (извлечение из карты) принимает необязательный аргумент для использования в случае отсутствия записи. Вместе эти функции поддаются краткому решению:

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

(defn replace-with-translation [s m]
  (string/replace s #":([-_a-zA-Z0-9]+)" 
    (fn [[r k]]
      (str (get m (keyword k) r)))))

PS Это регулярное выражение не полностью учитывает все возможные ключевые слова, но если оно удовлетворяет вашим требованиям, то оно, скорее всего, также работает в ClojureScript. .

2 голосов
/ 19 января 2020

Однострочный:

(reduce (fn [s [k v]] (str/replace s (str k) (str v)))
        "I am a test string. :name to replace, :age to replace. And there are strange symbols in this string. And here are the keyword shall not modify, such as :not-interested."
        {:name "Alice"
         :age  19})
;; => "I am a test string. Alice to replace, 19 to replace. And there are strange symbols in this string. And here are the keyword shall not modify, such as :not-interested."

Или вы можете сделать вашу таблицу перевода сначала строкой-> строкой. При этом вы можете избежать повторных вызовов на replace.

(let [translation-table {":name" "Alice"
                         ":age"  "19"}
      pat               (->> translation-table
                             keys
                             (str/join "|")
                             re-pattern)]
  (time
   (str/replace "I am a test string. :name to replace, :age to replace. And there are strange symbols in this string. And here are the keyword shall not modify, such as :not-interested."
                pat
                translation-table)))
;; "Elapsed time: 0.0258 msecs"
;; => "I am a test string. Alice to replace, 19 to replace. And there are strange symbols in this string. And here are the keyword shall not modify, such as :not-interested."

... предыдущая версия - 0.1345 msecs

1 голос
/ 19 января 2020

Вот угловой вопрос для вас.

Что если бы значение translation-table было {:not-in "foo"}?

Вы бы хотели, чтобы (replace-with-translation "such as :not-interested." translation-table) вернул:

(а) "such as footerested."

(б) "such as :not-interested."

(c) Что-то еще?

Если ответ (а), то я может показать вам, как реализовать это, используя один или несколько вызовов к clojure.string/replace.

. Если ответ (b), то он открывает все виды вопросов в угловых случаях о том, как вы хотите решить, является ли вхождение из символов ": not-in" должны совпадать или не должны совпадать в строке, где могут быть сделаны замены.

0 голосов
/ 19 января 2020

Вам нужно написать функцию для применения str/replace к каждой паре цели / значения на карте. Вот как бы я это сделал:

(ns tst.demo.core
  (:use tupelo.core tupelo.test)
  (:require
    [schema.core :as s]
    [tupelo.schema :as tsk]
    [clojure.string :as str]))


(def test-str "I am a test string. :name to replace, :age to replace. And there are strange symbols in this string. And here are the keyword shall not modify, such as :not-interested.")
(def another-test-str ":nothing :to :replace")

(def translation-table {:name "Alice"
                        :age  19})

(s/defn replace-with-translation :- s/Str
  [template-str :- s/Str
   tx-table :- tsk/KeyMap]
  (let [tx-tgts                (mapv pr-str (keys tx-table)) ; convert all keywords => string
        tx-vals                (mapv str (vals tx-table)) ; coerce any integers => string
        tx-pairs               (mapv vector tx-tgts tx-vals)
        ; ^^^ example:  tx-pairs => [[":name" "Alice"] [":age" "19"]]

        replace-placeholder-fn (fn [result tx-pair]
                                 (let [[tx-tgt tx-val] tx-pair]
                                   (str/replace result tx-tgt tx-val)))
        result-str             (reduce
                                 replace-placeholder-fn
                                 template-str
                                 tx-pairs)]
    result-str))

В модульных тестах код показан в действии:

(dotest
  (is= (replace-with-translation test-str translation-table)
    "I am a test string. Alice to replace, 19 to replace. And there are strange symbols in this string. And here are the keyword shall not modify, such as :not-interested.")
  (is= (replace-with-translation another-test-str translation-table)
    ":nothing :to :replace")
  )
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...