2012-02-23 2 views
32

Я потянув данные из Redis с помощью Алеф:Clojure: Преобразование хэш-карт ключевых строк в ключевые слова?

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

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

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

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

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

Я ранее создания новой карты с помощью:

{: имя (м «имя»),: как (м «как»)}

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

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

ответ

40

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

(keyword "foo") 
=> :foo 

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

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

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

Да выглядит как основная итерация является самым простым решением было проверить, есть ли стандартная функция для карт. Но его трудно отвлечь. Спасибо – dMix

+3

Существует стандартная функция (в основных библиотеках), которая будет делать это, см. Мой ответ ниже – djhworld

3

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

+4

являются ли ключи и vals гарантированно быть в одном порядке? – gtrak

+0

Не уверен. Я не знаю, как проверить. – viebel

+0

http://stackoverflow.com/questions/10772384/clojures-maps-are-keys-and-vals-in-same-order – Inshallah

73

Вы также можете использовать clojure.walk библиотеку, чтобы достичь желаемого результата с помощью функции keywordize-keys

(use 'clojure.walk) 
(keywordize-keys {"name" "Tylenol", "how" "instructions"}) 
;=> {:name "Tylenol", :how "instructions"} 

Это будет ходить карту рекурсивно, а так будет ключи «keywordize» в вложенной карте тоже

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

+2

Это не работает для вложенных карт для меня. –

4

Я согласен с 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 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))) 
0

Я второй @ mikera-х into based answer.С другой стороны, не самый лаконичный, но другой вариант, используя ассоциативный + dissoc/уменьшения будет:

(reduce #(dissoc (assoc %1 (keyword %2) (get %1 %2)) %2) my-map (keys may-map)) 
0

Возможно, стоит отметить, что, если входящие данные 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} 
Смежные вопросы