简体   繁体   English

在Clojure中重组Map。 将当前地图中的相应值转换为新地图

[英]Restructuring Map in Clojure. Make corresponding values in current map into new map

I am wondering what the most idiomatic approach to accomplish the following goal is. 我想知道实现以下目标的最惯用的方法是什么。 I'm just getting started with Clojure and I'm struggling with finding strategies for manipulating data structures that don't rely on traditional iteration constructs (for, while, etc.) 我刚刚开始使用Clojure,正在努力寻找策略来操纵不依赖传统迭代构造的数据结构(for,while等)。

If I have a map structure like the following: 如果我有如下地图结构:

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

And I would like to transform it into the following structure: 我想将其转换为以下结构:

{"Title1" "Value1", "Title2" "Value2"}

Essentially, I would like to make a new map whose keys are the values of the *title keys, and the values are the values of the corresponding *value keys. 本质上,我想创建一个新地图,其键是* title键的值,而值是相应* value键的值。

What would be the best clojure approach for doing this? 这样做的最佳Clojure方法是什么?

What I've attempted is the following (which I don't know if it will always work). 我尝试了以下方法(我不知道它是否将始终有效)。 It essentially extracts the *title values and then zips them with extracted *val values 本质上,它提取* 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))

This successfully extracts the keys and values, but I'm not sure if zipping these together with zipmap is the best way, or most idiomatic way to combine them. 这样可以成功提取键和值,但是我不确定将它们与zipmap一起压缩是最好的方法还是结合它们的最惯用的方法。

Zipmap will fail for larger maps because key/value pairs do not have a strict ordering inside a map. 对于较大的地图,Zipmap将失败,因为键/值对在地图内部没有严格的顺序。 For small maps, they often follow the order you created them because small maps are created as PersistentArrayMaps. 对于小型地图,它们通常遵循您创建它们的顺序,因为小型地图是作为PersistentArrayMaps创建的。 Large maps are PersistentHashMap. 大型地图是PersistentHashMap。 Observe the result of (apply hash-map (range 100)) vs (apply hash-map (range 10)) So if you have a bigger map, your -titles will not be aligned with your -vals 观察(apply hash-map (range 100)) vs (apply hash-map (range 10))因此,如果您有较大的map,则-titles不会与-vals对齐

Unfortunately this means you really need to look up vals that match your titles explicitly. 不幸的是,这意味着您确实需要查找与您的标题明确匹配的val。 Here is one way to do that: 这是一种方法:

(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))])))

For each key ending in title, look up the val with the same prefix, and put it all into a map. 对于每个以title结尾的键,请查找具有相同前缀的val,并将其全部放入地图中。

I would answer this question similarly to Timothy, but for production code I think it is best to spread things out some and be a bit more explicit: 我会像提摩西一样回答这个问题,但对于生产代码,我认为最好将内容分散开并更加明确:

(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} )))))

And of course, some unit tests: 当然,一些单元测试:

(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" } )))

Running the tests: 运行测试:

~/clj > lein test

lein test tst.clj.core

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

i would rather prefer using reduce-kv for that: 我宁愿为此使用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))

in repl: 代表:

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"}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM