用字符串替换 UUID

Replace the UUIDs with Strings

我想使用 Clojure 将 UUID 替换为字符串,但不确定如何为其创建函数。

这是原始值:/v1/user-1/4bcbe877-44fc-43c7-9cf4-aa3c2f8cc964/user-2/7badb866-44fc-43c7-9cf4-aa3c2f8cc964/user-3/27ebd241-44fc-43c7-9cf4-aa3c2f8cc964

并希望看到如下内容

/v1/user-1/user-1-id/user-2/user-2-id/user-3/user-3-id

但动态足以处理以下示例,

案例一

输入:

/v1/user-1/4bcbe877-44fc-43c7-9cf4-aa3c2f8cc964/user-2/7badb866-44fc-43c7-9cf4-aa3c2f8cc964/user-3/27ebd241-44fc-43c7-9cf4-aa3c2f8cc964/car

预期结果

/v1/user-1/user-1-id/user-2/user-2-id/user-3/user-3-id/car

案例二

输入:

/user-1/4bcbe877-44fc-43c7-9cf4-aa3c2f8cc964/user-2/7badb866-44fc-43c7-9cf4-aa3c2f8cc964/user-3/27ebd241-44fc-43c7-9cf4-aa3c2f8cc964/car

预期结果

/user-1/user-1-id/user-2/user-2-id/car

案例三

输入:

/user-1/4bcbe877-44fc-43c7-9cf4-aa3c2f8cc964/user-2/7badb866-44fc-43c7-9cf4-aa3c2f8cc964/user-3/27ebd241-44fc-43c7-9cf4-aa3c2f8cc964/car/tire

预期结果

/user-1/user-1-id/user-2/user-2-id/car/tire

这是一种方法,基于 my favorite template project

(ns tst.demo.core
  (:use tupelo.core tupelo.test)
  (:require
    [tupelo.string :as str]
    [tupelo.uuid :as uuid]
    ))

(defn replace-uuids
  [input-str]
  ; use `let-spy-pretty` below to see step-by-step results
  (let [segments       (str/split input-str #"/")
        pairs          (partition-all 2 segments)
        ; looks like:
        ;    [["" "v1"]
        ;     ["user-1" "4bcbe877-44fc-43c7-9cf4-aa3c2f8cc964"]
        ;     ["user-2" "7badb866-44fc-43c7-9cf4-aa3c2f8cc964"]
        ;     ["user-3" "27ebd241-44fc-43c7-9cf4-aa3c2f8cc964"]]
        version-pair   (first pairs)
        user-pairs     (rest pairs)
        user-pairs-mod (for [pair user-pairs]
                         (let [user-part (first pair)
                               uuid-part (second pair)]
                           (if (uuid/uuid-str? uuid-part)
                             [user-part (str user-part "-id")]
                             pair)))
        all-pairs-mod  (flatten [version-pair user-pairs-mod])
        result         (str/join (interpose "/" all-pairs-mod))]
    result))

(dotest
  (is= "/v1/user-1/user-1-id/user-2/user-2-id/user-3/user-3-id"
    (replace-uuids "/v1/user-1/4bcbe877-44fc-43c7-9cf4-aa3c2f8cc964/user-2/7badb866-44fc-43c7-9cf4-aa3c2f8cc964/user-3/27ebd241-44fc-43c7-9cf4-aa3c2f8cc964"))
  (is= "/v1/user-1/user-1-id/user-2/user-2-id/user-3/user-3-id/car"
    (replace-uuids "/v1/user-1/4bcbe877-44fc-43c7-9cf4-aa3c2f8cc964/user-2/7badb866-44fc-43c7-9cf4-aa3c2f8cc964/user-3/27ebd241-44fc-43c7-9cf4-aa3c2f8cc964/car"))
  )

对于更通用的解决方案,您可能只想使用正则表达式,这是 uuid-str? 内部所做的。

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

(def full-regex
  #"(?x)              # expanded form
  /                   # a slash
  ([a-zA-Z0-9-_]+)    # repeated identifier char in a capture group
  /                   # a slash
  \p{XDigit}{8}       # 8 hex digits
  -                   # hyphen
  \p{XDigit}{4}       # 4 hex digits
  -                   # hyphen
  \p{XDigit}{4}       # 4 hex digits
  -                   # hyphen
  \p{XDigit}{4}       # 4 hex digits
  -                   # hyphen
  \p{XDigit}{12}      # 12 hex digits
  ")

(s/defn replace-uuids :- s/Str
  [s :- s/Str]
  (str/replace s full-regex
    (fn [arg]
      (let ; use `let-spy` to see intermediate steps
        [x1     (second arg)
         result (str "/" x1 "/" x1 "-id")]
        result))))

结果

(dotest
  (is= "/v1/user-1/user-1-id/user-2/user-2-id/user-3/user-3-id"
    (replace-uuids "/v1/user-1/4bcbe877-44fc-43c7-9cf4-aa3c2f8cc964/user-2/7badb866-44fc-43c7-9cf4-aa3c2f8cc964/user-3/27ebd241-44fc-43c7-9cf4-aa3c2f8cc964"))
  (is= "/v1/user-1/user-1-id/user-2/user-2-id/user-3/user-3-id/car"
    (replace-uuids "/v1/user-1/4bcbe877-44fc-43c7-9cf4-aa3c2f8cc964/user-2/7badb866-44fc-43c7-9cf4-aa3c2f8cc964/user-3/27ebd241-44fc-43c7-9cf4-aa3c2f8cc964/car"))
  (is= "/v1/user-1/user-1-id/user-2/user-2-id/user-3/user-3-id/car/tire"
    (replace-uuids "/v1/user-1/4bcbe877-44fc-43c7-9cf4-aa3c2f8cc964/user-2/7badb866-44fc-43c7-9cf4-aa3c2f8cc964/user-3/27ebd241-44fc-43c7-9cf4-aa3c2f8cc964/car/tire"))
  )

另请参阅 list of documentation sources

Clojure Spec 对这类事情很方便。这是一种方法:

(require '[clojure.spec.alpha :as spec]
         '[clojure.string :as cljstr])

(spec/def ::fixkey-format
  (spec/cat :root #{""}
            :version #{"v1"}
            :pref1 #{"user-1"}
            :user-1 string?
            :pref2 #{"user-2"}
            :user-2 string?
            :pref3 #{"user-3"}
            :user-3 string?))

(defn replace-values [input new-values]
  (cljstr/join
   "/"
   (spec/unform
    ::fixkey-format
    (merge (spec/conform ::fixkey-format (cljstr/split input #"/")) new-values))))

(replace-values "/v1/user-1/4bcbe877-44fc-43c7-9cf4-aa3c2f8cc964/user-2/7badb866-44fc-43c7-9cf4-aa3c2f8cc964/user-3/27ebd241-44fc-43c7-9cf4-aa3c2f8cc964"
                {:user-1 "x"
                 :user-2 "y"
                 :user-3 "z"})
;; => "/v1/user-1/x/user-2/y/user-3/z"

或者只是字面上做简单的字符串替换

(clojure.string/replace
 "/v1/user-1/4bcbe877-44fc-43c7-9cf4-aa3c2f8cc964/user-2/7badb866-44fc-43c7-9cf4-aa3c2f8cc964/user-3/27ebd241-44fc-43c7-9cf4-aa3c2f8cc964"
 #"/(user-\d+)/[0-9a-f\-]+(?=/|$)"
 "//-id")

;;=> "/v1/user-1/user-1-id/user-2/user-2-id/user-3/user-3-id"

(defn normalize-path [path]
  (clojure.string/replace path #"/(user-\d+)/[0-9a-f\-]+(?=/|$)" "//-id"))

(map normalize-path
     ["/v1/user-1/4bcbe877-44fc-43c7-9cf4-aa3c2f8cc964/user-2/7badb866-44fc-43c7-9cf4-aa3c2f8cc964/user-3/27ebd241-44fc-43c7-9cf4-aa3c2f8cc964/car"
      "/user-1/4bcbe877-44fc-43c7-9cf4-aa3c2f8cc964/user-2/7badb866-44fc-43c7-9cf4-aa3c2f8cc964/user-3/27ebd241-44fc-43c7-9cf4-aa3c2f8cc964/car"
      "/user-1/4bcbe877-44fc-43c7-9cf4-aa3c2f8cc964/user-2/7badb866-44fc-43c7-9cf4-aa3c2f8cc964/user-3/27ebd241-44fc-43c7-9cf4-aa3c2f8cc964/car/tire"])

;;=> ("/v1/user-1/user-1-id/user-2/user-2-id/user-3/user-3-id/car"
;;    "/user-1/user-1-id/user-2/user-2-id/user-3/user-3-id/car"
;;    "/user-1/user-1-id/user-2/user-2-id/user-3/user-3-id/car/tire")

您还可以像这样更新正则表达式以仅匹配有效的 uid:

(defn normalize-path [path]
  (clojure.string/replace
   path
   #"/(user-\d+)/[0-9a-f]{8}-(?:[0-9a-f]{4}-){3}[0-9a-f]{12}(?=/|$)"
   "//-id"))

(map normalize-path
     ["/v1/user-1/4bcbe877-44fc-43c7-9cf4-aa3c2f8cc964/user-2/7badb866-44fc-43c7-9cf4-aa3c2f8cc964/user-3/27ebd241-44fc-43c7-9cf4-aa3c2f8cc964/car"
      "/user-1/4bcbe877-44fc-43c7-9cf4-aa3c2f8cc964/user-2/7badb866-44fc-43c7-9cf4-aa3c2f8cc964/user-3/27ebd241-44fc-43c7-9cf4-aa3c2f8cc964/car"
      "/user-1/4bcbe877-44fc-43c7-9cf4-aa3c2f8cc964/user-2/7badb866-44fc-43c7-9cf4-aa3c2f8cc964/user-3/27ebd241-44fc-43c7-9cf4-aa3c2f8cc964/car/tire"
      "/v2/user-101/asd-asd-asd/user-2/xxx"
      "/v3/user-404/aa44ddss-3333-4444-aaaa/user-505/4bcbe877-44fc-43c7-9cf4-aa3c2f8cc964"])

;; ("/v1/user-1/user-1-id/user-2/user-2-id/user-3/user-3-id/car"
;;  "/user-1/user-1-id/user-2/user-2-id/user-3/user-3-id/car"
;;  "/user-1/user-1-id/user-2/user-2-id/user-3/user-3-id/car/tire"
;;  "/v2/user-101/asd-asd-asd/user-2/xxx"
;;  "/v3/user-404/aa44ddss-3333-4444-aaaa/user-505/user-505-id")