Clojure: конвертировать хеш-карты с ключевыми строками в ключевые слова?
Я извлекаю данные из Redis с помощью Aleph:
(apply hash-map @(@r [:hgetall (key-medication id)]))
Проблема заключается в том, что эти данные возвращаются со строками для ключей, например:
({"name" "Tylenol", "how" "instructions"})
Когда мне это нужно:
({: name "Tylenol",: how "instructions})
Я ранее создавал новую карту с помощью:
{: name (m "name" ),: how (m "how" )}
Но это неэффективно для большого количества ключей.
Если есть функция, которая делает это? Или мне нужно перебирать каждый из них?
Ответы
Ответ 1
Существует удобная функция keyword, которая преобразует строки в соответствующие ключевые слова:
(keyword "foo")
=> :foo
Итак, это просто случай преобразования всех ключей на вашей карте с помощью этой функции.
Я бы, вероятно, использовал понимание списка с деструктуризацией, чтобы сделать это, например:
(into {}
(for [[k v] my-map]
[(keyword k) v]))
Ответ 2
Вы также можете использовать библиотеку 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
Ответ 3
Я согласен с 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)))
Ответ 4
Вы можете добиться этого очень элегантно, используя 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
позволяет создать карту, указав ключи и значения отдельно.
Ответ 5
I второй ответ @mikera into
. В качестве альтернативы, это не самый краткий, но другой вариант, использующий атрибут + disoc/reduce, будет следующим:
(reduce #(dissoc (assoc %1 (keyword %2) (get %1 %2)) %2) my-map (keys may-map))
Ответ 6
Возможно, стоит отметить, что если входящие данные 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}