2012-02-23 21 views
32

jestem ciągnięcie danych z Redis użyciu Aleph:Clojure: Konwertuj klucze hash-map na słowa kluczowe?

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

Problemem jest to dane wraca z ciągów na klucze, ex:

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

Kiedy muszę go mieć:

({: nazwa "Tylenol": jak „instrukcje})

byłem poprzednio tworzenia nowej mapy poprzez:

{: nazwa (m "nazwa"): w jaki sposób (m "jak")}

Ale to jest nieskuteczne dla dużej ilości klawiatura.

Jeśli istnieje funkcja, która to robi? Lub czy muszę przechodzić przez każdy z nich?

Odpowiedz

40

Jest przydatna funkcja nazywa keyword który przekształca Struny do odpowiednich słów kluczowych:

(keyword "foo") 
=> :foo 

Więc to tylko przypadek przekształcenia wszystkie klucze w mapie przy użyciu tej funkcji.

I pewnie użyć wyrażeń listowych z destructuring to zrobić, coś jak:

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

Tak wygląda podstawowy iteracja jest najprostszym rozwiązaniem, było sprawdzenie, czy istnieje standardowa funkcja map. Ale nie trudno to streścić. Dzięki – dMix

+3

Jest standardowa funkcja (w podstawowych bibliotekach), która to zrobi, zobacz moją odpowiedź poniżej – djhworld

3

Można to osiągnąć bardzo elegancko używając zipmap:

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

Zasadniczo zipmap pozwala na tworzenie mapę, określając oddzielnie klucze i wartości.

+4

są klucze i vals gwarantowane są w tej samej kolejności? – gtrak

+0

Nie jestem pewien. Nie wiem, jak sprawdzić. – viebel

+0

http://stackoverflow.com/questions/10772384/clojures-maps-are-keys-and-ale-iname-order- – Inshallah

73

Można również korzystać z biblioteki clojure.walk do osiągnięcia pożądanego rezultatu z funkcją keywordize-keys

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

To będzie chodzić mapę rekurencyjnie, jak również tak, że będzie „keywordize” klucze w zagnieżdżonej mapie zbyt

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

+2

Dla mnie nie działa to dla map zagnieżdżonych. –

4

Zgadzam się z djhworld, clojure.walk/keywordize-keys jest to, co chcesz.

Warto zerkając na kodzie źródłowym 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))) 

odwrotne przekształcenie jest czasami przydatne dla java współdziałanie:

(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

I drugi @ mikera na into based answer.Alternatywnie, nie jest to najbardziej zwięzły, ale inna opcja korzystania assoc + dissoc/zmniejszyć byłoby:

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

Może warto zauważyć, że jeśli dane wejściowe jest json i używasz clojure.data.json można określić zarówno key-fn i value-fn do manipulacji wyników na parsowania ciąg (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} 
Powiązane problemy