Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Clojure: Get All Nested Map Values (N levels deep)

DZone's Guide to

Clojure: Get All Nested Map Values (N levels deep)

· Java Zone
Free Resource

What every Java engineer should know about microservices: Reactive Microservices Architecture.  Brought to you in partnership with Lightbend.

I recently needed to pull all the values from a nested map. The map I was working with was designed to give easy access to data like so (formatted):

user=>
(def my-data {"Jay" 
                {"clojure" 
                  {:name "Jay", :language "clojure", :enjoys true}, 
                 "ruby" 
                  {:name "Jay", :language "ruby", :enjoys true}} 
              "Jon" 
                {"java" 
                  {:name "Jon", :language "java", :enjoys true}}} )
#'user/my-data
user=> (get-in my-data ["Jon" "java"])                                                                                                             
 {:name "Jon", :language "java", :enjoys true}            
user=> (get-in my-data ["Jay" "ruby"])                                                                                                             
 {:name "Jay", :language "ruby", :enjoys true} 
This worked for all of the ways the data was accessed, until someone asked for a list of all people and all languages.

I needed a function that grabbed all the values nested to a point in the map (grabbing only the values instead of the deepest map wouldn't have provided valuable information). I created the following function that should grab all the values up to the nesting level specified.
(defn nth-vals* [a i m]
  (if (and (map? m) (> i 0))
    (reduce into a (map (fn [v] (nth-vals* a (dec i) v)) (vals m)))
    (conj a m)))

(defn nth-vals [i m]
  (if (nil? m)
    {}
    (nth-vals* [] i m)))
The nth-vals function can be used as the following example shows. (assuming the same map for my-data) (formatted)
user=> (nth-vals 2 my-data)
[{:name "Jay", :language "clojure", :enjoys true} 
 {:name "Jay", :language "ruby", :enjoys true}
 {:name "Jon", :language "java", :enjoys true}]
For reference, here's what's returned if we reduce the map all the way to it's values.
user=> (nth-vals 3 my-data)
["Jay" "clojure" true "Jay" "ruby" true "Jon" "java" true]
That list of values may be helpful for someone else, but it wouldn't have solved our current problem.

And, if you're interested, here's what's returned if you ask for 1 level deep of values. (formatted, again)
user=> (nth-vals 1 my-data)
[{"clojure" {:name "Jay", :language "clojure", :enjoys true}, 
  "ruby" {:name "Jay", :language "ruby", :enjoys true}} 
 {"java" {:name "Jon", :language "java", :enjoys true}}]
I wouldn't at all be surprised if something like this already exists in Clojure core, but I haven't found it yet.

 

From http://blog.jayfields.com/2011/05/clojure-get-all-nested-map-values-n.html

Microservices for Java, explained. Revitalize your legacy systems (and your career) with Reactive Microservices Architecture, a free O'Reilly book. Brought to you in partnership with Lightbend.

Topics:

Opinions expressed by DZone contributors are their own.

THE DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

X

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}