Recursion 重组Clojure中的地图。将当前映射中的相应值转换为新映射

Recursion 重组Clojure中的地图。将当前映射中的相应值转换为新映射,recursion,clojure,iteration,Recursion,Clojure,Iteration,我想知道实现以下目标最惯用的方法是什么。我刚刚开始使用Clojure,我正在努力寻找不依赖传统迭代构造(for、while等)的数据结构操作策略 如果我的地图结构如下所示: (def test-map {:cat1-title "Title1", :cat1-val "Value1", :cat2-title "Title2", :cat2-val "Value2"}) 我想将其转换为以下结构: {"Title1" "Value1", "Title2" "Value2"} 本质上,我想制作一

我想知道实现以下目标最惯用的方法是什么。我刚刚开始使用Clojure,我正在努力寻找不依赖传统迭代构造(for、while等)的数据结构操作策略

如果我的地图结构如下所示:

(def test-map {:cat1-title "Title1", :cat1-val "Value1", :cat2-title "Title2", :cat2-val "Value2"})
我想将其转换为以下结构:

{"Title1" "Value1", "Title2" "Value2"}
本质上,我想制作一个新的地图,其键是*标题键的值,这些值是对应*值键的值

这样做的最佳clojure方法是什么

我尝试了以下几点(我不知道它是否总是有效)。它本质上提取*title值,然后用提取的*val值对其进行压缩

(let [titles
  (vals (filter #(re-matches #".*-title"
                             (str (key %)))
                test-map))
  values
  (vals (filter #(re-matches #".*-val"
                             (str (key %)))
                test-map))]
  (zipmap titles values))

这成功地提取了键和值,但我不确定使用zipmap将它们压缩在一起是最好的方式,还是最惯用的方式来组合它们。

zipmap对于较大的映射将失败,因为键/值对在映射中没有严格的顺序。对于小贴图,它们通常遵循您创建它们的顺序,因为小贴图是作为PersistentArrayMaps创建的。大型地图是永久性的地图。观察
(应用哈希映射(范围100))
vs
(应用哈希映射(范围10))
的结果,因此如果您有一个更大的映射,您的-title将不会与您的-vals对齐

不幸的是,这意味着您确实需要查找与标题明确匹配的VAL。有一种方法可以做到这一点:

(defn transform [m]
  (into {} (for [[k v] m
                 :let [title (name k)]
                 :when (.endsWith title "-title")
                 :let [val-name (clojure.string/replace title #"-title$" "-val")]]
             [v  (m (keyword val-name))])))

对于以标题结尾的每个键,查找具有相同前缀的val,并将其全部放在地图中。

我将以类似于Timothy的方式回答此问题,但对于生产代码,我认为最好分散一些内容,并更明确一些:

(ns clj.core
  (:require [clojure.string :as str] )
  (:use tupelo.core)) ; it->

(defn is-title-kw       [arg] (re-matches #".*-title" (name arg)))
(defn title-kw->val-kw  [arg] (it-> arg
                                    (name it)
                                    (str/replace it #"-title" "-val")
                                    (keyword it)))

(defn transform [map-arg]
  (let [title-kws (filter is-title-kw (keys map-arg)) ]
    (into {}
      (for [title-kw title-kws]
        (let [val-kw      (title-kw->val-kw title-kw)
              title-str   (title-kw map-arg)
              val-str     (val-kw   map-arg) ]
          {title-str val-str} )))))
当然,还有一些单元测试:

(ns tst.clj.core
  (:use clj.core 
        clojure.test 
        tupelo.core))

(def test-map   { :cat1-title "Title1", :cat1-val "Value1", 
                  :cat2-title "Title2", :cat2-val "Value2" } )

(deftest t-is-title-kw
  (is      (is-title-kw :cat1-title))
  (is      (is-title-kw :cat2-title))
  (is (not (is-title-kw :cat1-val)))
  (is (not (is-title-kw :cat2-val))))

(deftest t-title-kw->val-kw
  (is (= :cat1-val (title-kw->val-kw :cat1-title)))
  (is (= :cat2-val (title-kw->val-kw :cat2-title))))

(deftest t-transform
  (is (=  (transform test-map)
          { "Title1" "Value1", 
            "Title2" "Value2" } )))
运行测试:

~/clj > lein test

lein test tst.clj.core

Ran 3 tests containing 7 assertions.
0 failures, 0 errors.

我宁愿使用
reduce kv
来实现:

(defn transform [items-map]
  (reduce-kv (fn [result k v]
               (if-let [[_ name] (re-find #"^:(.+)-title$" (str k))]
                 (assoc result v (items-map (keyword (str name "-val"))))
                 result))
             {} items-map))
答复:

user> (def test-map {:cat-1-title "Title1", :cat-1-val "Value1", 
                     :cat2-title "Title2", :cat2-val "Value2", 
                     :cat3-title "Title3", :cat3-val "Value3"})
#'user/test-map
user> (transform test-map)
{"Title1" "Value1", "Title2" "Value2", "Title3" "Value3"}

你能发布一个尝试,让我们有更多的事情要做吗?大部分问题是我根本不知道如何处理它,但我可以用我尝试过的内容进行编辑。是的,即使你不太确定要去哪里,看到你迄今为止尝试过的东西还是很好的。