2012-02-23 31 views
32

estoy tirando de datos de Redis usando Alef:Clojure: ¿Convertir cadenas de claves hash en palabras clave?

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

El problema es estos datos se vuelva con cadenas de claves, por ejemplo:

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

Cuando lo necesito ser:

({: nombre de "Tylenol",: cómo "las instrucciones})

estaba creando previamente un nuevo mapa a través de:

{: Nombre (m "nombre"), ¿cómo (m "cómo")}

Pero esto no es eficiente para una gran cantidad de llaves.

Si hay una función que hace esto? ¿O tengo que pasar por cada uno?

Respuesta

40

Hay una práctica función llamada keyword que convierte cadenas en las palabras clave adecuadas:

(keyword "foo") 
=> :foo 

lo que es sólo un caso de transformación de todas las llaves en su mapa de utilizar esta función.

lo que probablemente utilice una lista por comprensión con la desestructuración de hacer esto, algo como:

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

Sí parece iteración básica es la solución más fácil, fue comprobar si había una función estándar para mapas. Pero no es difícil abstraer eso. Gracias – dMix

+3

Hay una función estándar (en las bibliotecas centrales) que lo hará, ver mi respuesta a continuación – djhworld

3

Esto se puede conseguir muy elegantemente usando zipmap:

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

Básicamente, zipmap permite crear un mapa especificando claves y valores por separado.

+4

¿Están las llaves y vals garantizados en el mismo orden? – gtrak

+0

No estoy seguro. No sé cómo verificarlo. – viebel

+0

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

73

También puede utilizar la biblioteca clojure.walk para lograr el resultado deseado con la función keywordize-keys

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

Este caminará el mapa de forma recursiva, así por lo que lo hará teclas "keywordize" en el mapa anidado demasiado

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

+2

Parece que no funciona para los mapas anidados para mí. –

4

Estoy de acuerdo con djhworld, clojure.walk/keywordize-keys es lo que quiere.

Vale la pena mira a escondidas en el código fuente de 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))) 

La transformada inversa es a veces útil para la interoperabilidad de 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))) 
0

Me segundos @ mikera de into based answer.Por otra parte, no es la más concisa, pero, otra opción usando Assoc + dissoc/reducir sería:

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

Tal vez vale la pena señalar que, si los datos de entrada es json y está utilizando clojure.data.json, se puede especificar tanto una key-fn y una value-fn para manipular los resultados de analizar la cadena (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} 
Cuestiones relacionadas