从Clojure记录中返回普通地图

时间:2014-07-27 04:08:27

标签: clojure records

我有一个记录:

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

现在我想从记录中提取地图。这些方式可以完成工作。这些好方法吗?还有更好的方法吗?

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

我希望可能有一种更清洁的方式,也许内置于记录的概念中。

3 个答案:

答案 0 :(得分:3)

记录地图

(defrecord Thing [a b])

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

因此,通常不需要将它们强制转换为APersistentMap类型。但是,如果需要,可以使用into

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

如果要遍历任意数据结构(包括嵌套记录)进行此转换,请使用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}}}])

答案 1 :(得分:0)

A. Webb在评论中提出了更为简单的(into {} p)。谢谢!

这是一个更通用的代码片段;它适用于递归记录:

(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}}

答案 2 :(得分:0)

我还写了一个通用函数,它将记录转换为(大多数)任意Clojure数据结构的映射:

(defn derecordize
  "Returns a data structure equal (using clojure.core/=) to the
  original value with all records converted to plain maps."
  [v]
  (cond
    (record? v) (derecordize (into {} v))
    (list? v) (map derecordize v)
    (vector? v) (mapv derecordize v)
    (set? v) (set (map derecordize v))
    (map? v) (zipmap (map derecordize (keys v)) (map derecordize (vals v)))
    :else v))

我知道这很不寻常。当您需要导出没有记录的数据结构时,这非常有用。