如何在 clojure 中根据数字拆分字符串并将其转换为地图

How to split string in clojure on number and convert it to map

我有一个字符串 school_name_1_class_2_city_name_3 想在 clojure 中将它拆分为 {school_name: 1, class:2, city_name: 3} 我试过这段代码但没有用

(def s "key_name_1_key_name_2")
(->> s
     (re-seq #"(\w+)_(\d+)_")
     (map (fn [[_ k v]] [(keyword k) (Integer/parseInt v)]))
     (into {}))

您正在寻找正则表达式的非贪婪版本。

请尝试使用 #"(\w+?)_(\d+)_?"

user=> (->> s (re-seq #"(\w+?)_(\d+)_?"))
(["key_name_1_" "key_name" "1"] ["key_name_2" "key_name" "2"])

遇到问题,把它分解,一步一个脚印地解决。使用 the Tupelo library 中的 let-spy-pretty 可以让我们看到转换的每个步骤:

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

(defn str->keymap
  [s]
  (let-spy-pretty
    [str1 (re-seq #"([a-zA-Z_]+|[0-9]+)" s)
     seq1 (mapv first str1)
     seq2 (mapv #(str/replace % #"^_+" "") seq1)
     seq3 (mapv #(str/replace % #"_+$" "") seq2)
     map1 (apply hash-map seq3)
     map2 (tupelo.core/map-keys map1 #(keyword %) )
     map3 (tupelo.core/map-vals map2 #(Long/parseLong %) )]
    map3))

(dotest
  (is= (str->keymap "school_name_1_class_2_city_name_3")
    {:city_name 3, :class 2, :school_name 1}))

结果

------------------------------------
   Clojure 1.10.3    Java 11.0.11
------------------------------------

Testing tst.demo.core
str1 => 
(["school_name_" "school_name_"]
 ["1" "1"]
 ["_class_" "_class_"]
 ["2" "2"]
 ["_city_name_" "_city_name_"]
 ["3" "3"])
seq1 => 
["school_name_" "1" "_class_" "2" "_city_name_" "3"]
seq2 => 
["school_name_" "1" "class_" "2" "city_name_" "3"]
seq3 => 
["school_name" "1" "class" "2" "city_name" "3"]
map1 => 
{"city_name" "3", "class" "2", "school_name" "1"}
map2 => 
{:city_name "3", :class "2", :school_name "1"}
map3 => 
{:city_name 3, :class 2, :school_name 1}

Ran 2 tests containing 1 assertions.
0 failures, 0 errors.

Passed all tests

一旦您理解了这些步骤并且一切正常,只需将 let-spy-pretty 替换为 let 并继续!

这是使用 my favorite template project 构建的。

给定

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

(def s "school_name_1_class_2_city_name_3")

遵循已接受的答案:

(->> s (re-seq #"(.*?)_(\d+)_?") 
       (map rest) ;; take only the rest of each element 
       (map (fn [[k v]] [k (Integer. v)])) ;; transform second as integer
       (into {})) ;; make a hash-map out of all this

或者:

(apply hash-map ;; the entire thing as a hash-map
       (interleave (str/split s #"_(\d+)(_|$)") ;; capture the names 
                   (map #(Integer. (second %))  ;; convert to int
                         (re-seq #"(?<=_)(\d+)(?=(_|$))" s)))) ;; capture the integers

或:

(zipmap
  (str/split s #"_(\d+)(_|$)")   ;; extract names
  (->> (re-seq #"_(\d+)(_|$)" s) ;; extract values
       (map second)              ;; by taking only second matched groups
       (map #(Integer. %))))     ;; and converting them to integers
  • str/split 省略了匹配的部分
  • re-seq returns只有匹配的部分
  • (_|$) 确保数字后跟 _ 或在结束位置

最不冗长(其中 (_|$) 可以替换为 _?:

(->> (re-seq #"(.*?)_(\d+)(_|$)" s)        ;; capture key vals 
     (map (fn [[_ k v]] [k (Integer. v)])) ;; reorder coercing values to int
     (into {}))                            ;; to hash-map