Example:
Taking a vector of maps that looks like
[{:fruit "apple" :color "red" :brand "Granny Smith"}
{:fruit "banana" :color "yellow" :brand "Banana shop"}]
and converting it into {:fruit :color}
({"apple" "red"} {"banana" "yellow"})
Create a hash map from an even list of data
(defn create-hash-map [data] (apply hash-map data))
Output
:> (create-hash-map [1 2 3 4 5 6])
{1 2, 3 4, 5 6}
:> (create-hash-map [1 2 3 4 5])
; Execution error (IllegalArgumentException) at ns/create-hash-map (REPL:31).
; No value supplied for key: 5
Extract a key and value from a map to form a tupled map
(defn tuple [key value data]
(-> data
(select-keys [key value])
(vals)
(create-hash-map)))
Output
:>Â (tuple :fruit :color {:fruit "apple" :color "red" :brand "Granny Smith"})
{"apple" "red"}
Map through a list of maps to create tuples for each item.
(map
#(tuple :fruit :color %)
[{:fruit "apple" :color "red" :brand "Granny Smith"}
{:fruit "banana" :color "yellow" :brand "South African Bananas"}])
Output
({"apple" "red"} {"banana" "yellow"})
Top comments (2)
Hi Clarice. 👋 Nice post, and great, clear examples!
One question though—I think it's a little unusual to want data in this format:
...from that original structure. I'm trying to think of a use case...maybe we have a user-defined list of settings that we want to extract or something?
But since we're clearly planning to process this sequentially (we used a sequential collection, after all), having maps with a single entry rarely seems useful—we likely don't have known keys to look up (to leverage the associative nature of the collection), and if we're going to consume everything in the map (i.e. the one map entry), then we kinda made it a map for nothing. Hopefully that makes sense. I'd love to know where this would apply, if you have an example!
If you definitely do want to do this, I'd suggest simply:
...which will save you a bunch of intermediate map construction / destruction / resultant garbage collection.
(I've changed your argument names here to slightly more idiomatic ones, and changed the order to match conventions as well—functions taking maps typically receive them as the first argument so we can thread them with
->
(seeassoc
,select-keys
, etc.) whereas functions taking sequential collections (map
,filter
,reduce
, etc.) typically take them as their final argument so we can thread with->>
. It's a little thing we don't always notice, but tend to appreciate when it makes our code come together neatly!).In (what I think is) the more common case, where we want to, say, map every fruit to its color, a tidy solution is:
Or, if you're comfortable with transducers:
But if you are intending to just process them sequentially (e.g. if we're setting environment variables from those settings above), that's often just:
...and then we consume those pair-wise.
Anyway, I hope that's helpful. I find Clojure always has a lesson to teach me about simplicity. Cheers.
Hello Cameron, I seem to have missed the notification that you sent me a message. My sincerest apologies.
I love the simplicity you share in your comment! Thank for you doing so. I can't for the life of me remember why I needed to do that manipulation so I can't offer a real-world scenario at this stage. Your examples make for excellent documentation to this "how to" guide. Keep things, simple made easy ;)
Rock on!