Search code examples
functionclojure

Getting an nth element of a nested map


I would like to get the nth element of the nested map, but could not find the appropriate command and had got an error. I would need something that worked like a first function:

boot.user=>   (def db2 @main/database)
#'boot.user/db2
boot.user=> (def ims (get-in db2 [kl :items]))
#'boot.user/ims
boot.user=> ims
{"i15059" {:name "ESS1", :done? false}, "i15064" {:name "ESS2", :done? false}, "i15114" {:name "ESS3", :done? false}, "i15121" {:name "ESS3", :done? false}}
boot.user=> (first ims)
["i15059" {:name "ESS1", :done? false}]
boot.user=> (nth ims 1)

java.lang.UnsupportedOperationException: nth not supported on this type: PersistentArrayMap
boot.user=> 

Any suggestions?


Solution

  • A map is unordered (unless you explicitly created a sorted-map). When you get the map entries (such as a for loop or first), Clojure silently calls seq on the map to get a "list-like" sequence of MapEntry items:

    (def mappy {:c 3 :d 4 :a 1 :b 2 })
    
    mappy          => {:c 3, :d 4, :a 1, :b 2}
    (seq mappy)    => ([:c 3] [:d 4] [:a 1] [:b 2])
    (vec mappy)    => [[:c 3] [:d 4] [:a 1] [:b 2]]
    (first mappy)  => [:c 3]
    

    Note that a seq prints like a list, but isn't. A MapEntry prints like a vector, but isn't.

    (seq mappy)    => <#clojure.lang.PersistentArrayMap$Seq ([:c 3] [:d 4] [:a 1] [:b 2])>
    
    (first mappy)  => <#clojure.lang.MapEntry [:c 3]>
    

    Once you have some sequential object (vector, list, or seq), you can use nth to get the items one at a time:

    (let [mapseq (seq mappy)
          mapvec (vec mappy) ]
    
      (nth mapseq 3) => [:b 2]
      (nth mapvec 3) => [:b 2]
    

    However, note that the returned item is still a MapEntry, not a 2-vector.

      (nth mapseq 3) => <#clojure.lang.MapEntry [:b 2]>
      (nth mapvec 3) => <#clojure.lang.MapEntry [:b 2]>
    

    You can get the key and value from the MapEntry using key and val functions:

    (key (nth mapseq 3)) => :b
    (val (nth mapvec 3)) => 2
    

    P.S. You can print a value & its type as above using the spyxx function.