有没有更好的方法来访问Clojure中的嵌套地图和向量?

时间:2012-12-07 04:52:26

标签: clojure clojure-core.logic

我从here on freebase抓取一些json(小心,你只能在不使用&key=your-key的情况下请求这几次。)

我想将响应转换为类似的内容:

    ({:case "Roe v. Wade", :plaintiffs ("Norma McCorvey"), :defendants ("Henry Wade"), :court "Supreme Court of the United States", :subjects ("Abortion" "Privacy"), :article "http://wp/en/68493"} ...)

以下是使用clojure.data.json / read-string后我想出的代码:

    (defn extract-data [case]
      {:case (case "name")
       :plaintiffs (flatten (map #(get % "parties") (filter (fn [p] (some #(= (% "id") "/en/plaintiff") (p "role")))
                                                           (case "/law/legal_case/parties"))))
       :defendants (flatten (map #(get % "parties") (filter (fn [p] (some #(= (% "id") "/en/defendant") (p "role")))
                                                           (case "/law/legal_case/parties"))))
       :court (get-in case ["court" 0 "name"])
       :subjects (map #(% "name") (case "subject"))
       :article (get-in case ["/common/topic/article" 0 "source_uri" 0])})

    (def response (-> query-uri
                       java.net.URL.
                       slurp
                       json/read-str))
    (def case-data (map extract-data (response "result")))

提取 - 数据似乎过于复杂,有没有更好的方法来做到这一点?这是一个可以使用core.logic的情况吗?如果是这样,怎么样?

2 个答案:

答案 0 :(得分:2)

您可能会看一下不同的查询系统(zip-filters,core.logic,datomic的数据记录集合等)。或者自己动手:

(defn select [x path]
  (if-let [[p & ps] (seq path)]
    (if (fn? p)
      (mapcat #(select % ps) (filter p x))
      (recur (get x p) ps))
    x))

(def mapping
  {:case ["name"]
   :plaintiffs ["/law/legal_case/parties"
                #(= (get-in % ["role" 0 "id" 0]) "/en/plaintiff")
                "parties"]
   :defendants ["/law/legal_case/parties"
                #(= (get-in % ["role" 0 "id" 0]) "/en/defendant")
                "parties"]
   :court ["court" 0 "name" 0]
   :subjects ["subject" (constantly true) "name"]
   :article ["/common/topic/article" 0 "source_uri" 0]})

(defn extract-data [x mapping]
  (into {}
    (for [[k path] mapping]
      [k (if (some fn? path) (select x path) (get-in x path))])))

然后(map#(extract-data%mapping)结果)应该做的伎俩

=> (extract-data (first result) mapping)
{:case "Roe v. Wade", :plaintiffs ("Norma McCorvey"), :defendants ("Henry Wade"), :court "Supreme Court of the United States", :subjects ("Abortion" "Privacy"), :article "http//wp/en/68493"}

这种类型的代码(查询解释器)可能很脆弱,所以一定要有一个测试套件。

答案 1 :(得分:0)

Specter可以提供帮助。

(ns stackoverflow-answer.core
  (:require [com.rpl.specter :refer :all]))

; your example data
(def case {"name" "Roe v. Wade"
           "/law/legal_case/parties" [{"role" [{"id" "/en/plaintiff"}]
                                       "parties" ["Norma McCorvey"]}
                                      {"role" [{"id" "/en/defendant"}]
                                       "parties" ["Henry Wade"]}]
           "court" [{"name" "Supreme Court of the United States"}]
           "subject" [{"name" "Abortion"}
                      {"name" "Privacy"}]
           "/common/topic/article" [{"source_uri" ["http://wp/en/68493"]}]})

(defn specter-extract [case]
  {:case (case "name")
   :plaintiffs (select ["/law/legal_case/parties"
                        ALL
                        (selected? "role" ALL "id" (pred= "/en/plaintiff"))
                        "parties"
                        ALL]
                       case)
   ; etc
   })

用法:

stackoverflow-answers.core=> (specter-extract case)
{:case "Roe v. Wade", :plaintiffs ["Norma McCorvey"]}