Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Return plain map from a Clojure record

I have a record:

(defrecord Point [x y])
(def p (Point. 1 2))

Now I want to extract just the map from the record. These ways get the job done. Are these good ways? Are there better ways?

(into {} (concat p))
(into {} (map identity p))
(apply hash-map (apply concat p))

I was hoping there might be a cleaner way, perhaps built-in to the notion of a record.

like image 890
David J. Avatar asked Mar 19 '23 22:03

David J.


2 Answers

Records are maps

(defrecord Thing [a b])

(def t1 (Thing. 1 2))
(instance? clojure.lang.IPersistentMap t1) ;=> true

So, in general there is no need to coerce them into a APersistentMap type. But, if desired you do so with into:

(into {} t1) ;=> {:a 1, :b 2}

If you want to traverse an arbitrary data structure, including nested records, making this transformation, then use walk

(def t2 (Thing. 3 4))
(def t3 (Thing. t1 t2))
(def coll (list t1 [t2 {:foo t3}]))

(clojure.walk/postwalk #(if (record? %) (into {} %) %) coll)
;=> ({:a 1, :b 2} [{:a 3, :b 4} {:foo {:a {:a 1, :b 2}, :b {:a 3, :b 4}}}])
like image 184
A. Webb Avatar answered Mar 27 '23 10:03

A. Webb


A. Webb suggested the much-simpler (into {} p) in the comments. Thanks!

Here is a code snippet that is more general; it works for recursive records:

(defrecord Thing [a b])
(def t1 (Thing. 1 2))
(def t2 (Thing. 3 4))
(def t3 (Thing. t1 t2))

(defn record->map
  [record]
  (let [f #(if (record? %) (record->map %) %)
        ks (keys record)
        vs (map f (vals record))]
    (zipmap ks vs)))

(record->map t3)
; {:b {:b 4, :a 3}, :a {:b 2, :a 1}}
like image 44
David J. Avatar answered Mar 27 '23 11:03

David J.