【问题标题】:How to split string in clojure on number and convert it to map如何在数字上拆分clojure中的字符串并将其转换为映射
【发布时间】:2021-10-07 03:24:13
【问题描述】:

我有一个字符串 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 {}))

【问题讨论】:

    标签: clojure


    【解决方案1】:

    您正在寻找不贪婪的正则表达式。

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

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

    【讨论】:

    • 如果我有负数,这个表达式失败,如果我想转换这个字符串 "key_name_1_key_name_-2" 它会忽略负数,我将正则表达式更新为这个 #"(\w+?)_-?( \d+)_?"但这是将负数转换为正数,任何保留符号的建议
    • 这个表达式解决了它 "(\w+?)_([+-]?\d+)_?
    【解决方案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 构建的。

    【讨论】:

      【解决方案3】:

      给定

      (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 只返回匹配的部分
      • (_|$) 确保数字后跟 _ 或位于结束位置

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2010-11-01
        • 2022-01-08
        • 1970-01-01
        • 2012-04-02
        • 2011-08-03
        • 1970-01-01
        • 2018-04-18
        • 1970-01-01
        相关资源
        最近更新 更多