Clojure: конвертировать ключевые строки хеш-карт в ключевые слова? - PullRequest
33 голосов
/ 23 февраля 2012

Я извлекаю данные из Redis, используя Aleph:

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

Проблема в том, что эти данные возвращаются со строками для ключей, например:

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

Когда мне нужно, чтобы это было:

({: имя "Тайленол",: как "инструкции})

Ранее я создавал новую карту с помощью:

{: name (m "name"),: how (m "how")}

Но это неэффективно для большого количества ключей.

Если есть функция, которая делает это? Или я должен проходить через каждый?

Ответы [ 6 ]

78 голосов
/ 23 февраля 2012

Вы также можете использовать библиотеку 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

41 голосов
/ 23 февраля 2012

Существует удобная функция с именем ключевое слово , которая преобразует строки в соответствующие ключевые слова:

(keyword "foo")
=> :foo

Так что это всего лишь случай преобразования всех ключей на карте с помощью этой функции.

Я бы, вероятно, использовал для этого понимание списков с деструктурированием, что-то вроде:

(into {} 
  (for [[k v] my-map] 
    [(keyword k) v]))
5 голосов
/ 28 мая 2014

Я согласен с 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)))
3 голосов
/ 23 февраля 2012

Вы можете достичь этого очень элегантно, используя 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 голос
/ 17 октября 2017

Возможно, стоит отметить, что если входящие данные json и вы используете clojure.data.json, вы можете указать как key-fn, так и value-fn для управления результатами при разборе строки ( документы ) -

;; 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}
0 голосов
/ 05 сентября 2017

Я второй @ mikera's into ответ на основе . В качестве альтернативы, не самый краткий, но другой вариант с использованием Assoc + Disococ / Reduce будет:

(reduce #(dissoc (assoc %1 (keyword %2) (get %1 %2)) %2) my-map (keys may-map))
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...