问题
I'm pulling data from Redis using Aleph:
(apply hash-map @(@r [:hgetall (key-medication id)]))
The problem is this data comes back with strings for keys, for ex:
({"name" "Tylenol", "how" "instructions"})
When I need it to be:
({:name "Tylenol", :how "instructions})
I was previously creating a new map via:
{ :name (m "name"), :how (m "how")}
But this is inefficient for a large amount of keys.
If there a function that does this? Or do I have to loop through each?
回答1:
There is a handy function called keyword that converts Strings into the appropriate keywords:
(keyword "foo")
=> :foo
So it's just a case of transforming all the keys in your map using this function.
I'd probably use a list comprehension with destructuring to do this, something like:
(into {}
(for [[k v] my-map]
[(keyword k) v]))
回答2:
You can also use the clojure.walk
library to achieve the desired result with the function keywordize-keys
(use 'clojure.walk)
(keywordize-keys {"name" "Tylenol", "how" "instructions"})
;=> {:name "Tylenol", :how "instructions"}
This will walk the map recursively as well so it will "keywordize" keys in nested map too
http://clojuredocs.org/clojure_core/clojure.walk/keywordize-keys
回答3:
I agree with djhworld, clojure.walk/keywordize-keys
is what you want.
It's worth peeking at the source code of 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)))
The inverse transform is sometimes handy for java interop:
(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)))
回答4:
You can achieve this very elegantly using zipmap
:
(defn modify-keys [f m] (zipmap (map f (keys m)) (vals m)))
(modify-keys keyword {"name" "Tylenol", "how" "instructions"})
; {:how "instructions", :name "Tylenol"}
Basically, zipmap
allows to create a map by specifying keys and values separately.
回答5:
Perhaps it is worth noting that, if the incoming data is json
and you are using clojure.data.json
, you can specify both a key-fn
and a value-fn
for manipulating results on parsing the string (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:
I second @mikera's into based answer. Alternatively, not the most concise but, another option using assoc+dissoc/reduce would be:
(reduce #(dissoc (assoc %1 (keyword %2) (get %1 %2)) %2) my-map (keys may-map))
回答7:
Using the keyword
function and reduce-kv
.
If I have a map e.g.
(def test-map {"key1" "val1" "key2" "val2"}
I can do
(reduce-kv
(fn [m k v]
(assoc m (keyword k) v))
{} test-map)
=> {:key1 "val1" :key2 "val2"}
来源:https://stackoverflow.com/questions/9406156/clojure-convert-hash-maps-key-strings-to-keywords