41

I'm pulling data from Redis using Aleph:

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

The problem is this data comes back with strings for keys, for ex:

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

When I need it to be:

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

I was previously creating a new map via:

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

But this is inefficient for a large amount of keys.

If there a function that does this? Or do I have to loop through each?

7 Answers 7

94

You can also use the clojure.walk library to achieve the desired result with the function keywordize-keys

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

This will walk the map recursively as well so it will "keywordize" keys in nested map too

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

Sign up to request clarification or add additional context in comments.

1 Comment

Most importantly, this works for nested maps!
47

There is a handy function called keyword that converts Strings into the appropriate keywords:

(keyword "foo")
=> :foo

So it's just a case of transforming all the keys in your map using this function.

I'd probably use a list comprehension with destructuring to do this, something like:

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

2 Comments

Yeah looks like basic iteration is the easiest solution, was checking if there was a standard function for maps. But its not hard to abstract that. Thanks
There is a standard function (in the core libs) that will do it, see my answer below
9

I agree with djhworld, clojure.walk/keywordize-keys is what you want.

It's worth peeking at the source code of 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)))

The inverse transform is sometimes handy for 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)))

Comments

7

Perhaps it is worth noting that, if the incoming data is json and you are using clojure.data.json, you can specify both a key-fn and a value-fn for manipulating results on parsing the string (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}

Comments

6

You can achieve this very elegantly using zipmap:

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

Basically, zipmap allows to create a map by specifying keys and values separately.

5 Comments

are keys and vals guaranteed to be in the same order?
Not sure. I don't know how to check.
Clojure doc states that (keys map) and (vals map) function results are ordered with the same order as the (seq map) function. Thus, by associativity, they all have the same order. :) @gtrak
This is the most elegant solution
1

Using the keyword function and reduce-kv.

If I have a map e.g.

 (def test-map {"key1" "val1" "key2" "val2"}

I can do

(reduce-kv 
     (fn [m k v] 
        (assoc m (keyword k) v)) 
      {} test-map)

 => {:key1 "val1" :key2 "val2"}

Comments

0

I second @mikera's into based answer. Alternatively, not the most concise but, another option using assoc+dissoc/reduce would be:

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.