如何在Clojure

时间:2017-05-27 18:23:16

标签: clojure datomic

我使用的是Datomic,尽管这个问题并不特别重要。但它通常返回命名空间键(并且枚举值也作为命名空间关键字返回)。我想翻译潜在的嵌套结构,从键和值中去除命名空间(以及字符串 - 如果是枚举值)。我之所以这样做,是因为我会在JSON REST API中返回结果,并且命名空间在该上下文中没有多大意义。这是一个简单的示例结构:

{ 
    :person/name "Kevin"
    :person/age 99
    :person/gender :gender/M
    :person/address {
        :address/state :state/NY
        :address/city "New York"
        :address/zip "99999"
    }
}

我希望翻译成:

{ 
    :name "Kevin"
    :age 99
    :gender "M"
    :address {
        :state "NY"
        :city "New York"
        :zip "99999"
    }
}

我知道我可以做的一件事是使用(postwalk-replace {:person/name :name :person/age :age :person/gender :gender :person/address :address :address/city :city :address/state :state :address/zip :zip} the-entity),它涵盖了键,但不包括值。

我还有其他选择吗?

2 个答案:

答案 0 :(得分:3)

您可以使用clojure.walk/postwalk。简单版本不区分关键字作为地图中的键或值,只需将所有键转换为字符串:

(def data {:person/name "Kevin"
           :person/age 99
           :person/gender :gender/M
           :person/address {:address/state :state/NY
                            :address/city "New York"
                            :address/zip "99999"}})


(clojure.walk/postwalk
  (fn [x]
    (if (keyword? x)
      (name x)
      x))
  data)

;; => => {"name" "Kevin", "age" 99, "gender" "M", "address" {"state" "NY", "city" "New York", "zip" "99999"}}

要实现您想要的功能,您需要分别处理地图中的键和值:

(defn transform-keywords [m]
  (into {}
        (map (fn [[k v]]
               (let [k (if (keyword? k) (keyword (name k)) k)
                     v (if (keyword? v) (name v) v)]
                 [k v]))
             m)))

(clojure.walk/postwalk
  (fn [x]
    (if (map? x)
      (transform-keywords x)
      x))
  data)

;; => => {:name "Kevin", :age 99, :gender "M", :address {:state "NY", :city "New York", :zip "99999"}}

答案 1 :(得分:3)

作为旁注:根据我的经验,在系统边界处的命名空间限定键和非命名空间限定键之间的阻抗不匹配可能是一个持续的痛苦;更重要的是,拥有命名空间限定密钥在代码清晰度方面具有显着优势(非常好的数据可追溯性)。

所以我不会太容易放弃名称空间限定的密钥。如果EDN的命名空间(带点和斜线)的语法不适合您的API的使用者,您甚至可能希望使用更像传统的下划线(例如:person/name而不是{{1} }});有点丑陋,但它仍然提供了命名空间限定键的大部分好处,你甚至不需要转换数据结构,而Datomic不会介意。