Clojure:将哈希映射键字符串转换为关键字?

问题描述 投票:0回答:7

我正在使用 Aleph 从 Redis 中提取数据:

(apply hash-map @(@r [:hgetall (key-medication id)]))

问题是此数据返回时带有键字符串,例如:

({"name" "Tylenol", "how" "instructions"})

当我需要它时:

({:名称“泰诺”,:如何“说明})

我之前通过以下方式创建了新地图:

{ :name (m “名字”), :how (m “如何”)}

但是对于大量的密钥来说这效率很低。

是否有一个函数可以做到这一点?或者我必须循环遍历每个?

clojure
7个回答
93
投票

您还可以使用

clojure.walk
库通过函数
keywordize-keys

来实现所需的结果
(use 'clojure.walk)
(keywordize-keys {"name" "Tylenol", "how" "instructions"})
;=> {:name "Tylenol", :how "instructions"}

这也会递归地遍历地图,因此它也会对嵌套地图中的键进行“关键字化”

http://clojuredocs.org/clojure_core/clojure.walk/keywordize-keys


47
投票

有一个名为 keyword 的方便函数,可以将字符串转换为适当的关键字:

(keyword "foo")
=> :foo

所以这只是使用此函数转换地图中所有键的情况。

我可能会使用带有解构的列表理解来做到这一点,比如:

(into {} 
  (for [[k v] my-map] 
    [(keyword k) v]))

9
投票

我同意djhworld,

clojure.walk/keywordize-keys
就是你想要的。

值得一看

clojure.walk/keywordize-keys
的源代码:

(defn keywordize-keys
  "Recursively transforms all map keys from strings to keywords."
  [m]
  (let [f (fn [[k v]] (if (string? k) [(keyword k) v] [k v]))]
    (clojure.walk/postwalk (fn [x] (if (map? x) (into {} (map f x)) x)) m)))

逆变换有时对于 Java 互操作来说很方便:

(defn stringify-keys
  "Recursively transforms all map keys from keywords to strings."
  [m]
  (let [f (fn [[k v]] (if (keyword? k) [(name k) v] [k v]))]
    (clojure.walk/postwalk (fn [x] (if (map? x) (into {} (map f x)) x)) m)))

7
投票

也许值得注意的是,如果传入数据是

json
并且您正在使用
clojure.data.json
,则可以指定
key-fn
value-fn
来操作解析字符串的结果 (docs) -

;; Examples from the docs

(ns example
  (:require [clojure.data.json :as json]))


(json/read-str "{\"a\":1,\"b\":2}" :key-fn keyword) 
;;=> {:a 1, :b 2}

(json/read-str "{\"a\":1,\"b\":2}" :key-fn #(keyword "com.example" %))
;;=> {:com.example/a 1, :com.example/b 2}

6
投票

您可以使用

zipmap
非常优雅地实现这一点:

(defn modify-keys [f m] (zipmap (map f (keys m)) (vals m)))
(modify-keys keyword {"name" "Tylenol", "how" "instructions"})
; {:how "instructions", :name "Tylenol"}

基本上,

zipmap
允许通过分别指定键和值来创建映射。


1
投票

使用

keyword
功能和
reduce-kv

如果我有地图,例如

 (def test-map {"key1" "val1" "key2" "val2"}

我可以做

(reduce-kv 
     (fn [m k v] 
        (assoc m (keyword k) v)) 
      {} test-map)

 => {:key1 "val1" :key2 "val2"}

0
投票

我第二个@mikera基于

into
的答案。或者,不是最简洁的,而是使用 assoc+dissoc/reduce 的另一个选项是:

(reduce #(dissoc (assoc %1 (keyword %2) (get %1 %2)) %2) my-map (keys may-map))
© www.soinside.com 2019 - 2024. All rights reserved.