Есть ли лучший способ доступа к вложенным картам и векторам в Clojure?

Я беру 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")))

Extract-data кажется слишком сложным, есть ли лучший способ сделать это? Это тот случай, когда можно использовать core.logic? Если да, то как?


person bmaddy    schedule 07.12.2012    source источник


Ответы (2)


Вы можете посмотреть различные системы запросов (zip-фильтры, 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"}

Этот тип кода (интерпретатор запросов) может быть ненадежным, поэтому убедитесь, что у вас есть набор тестов.

person cgrand    schedule 07.12.2012

Spectre может помочь.

(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"]}
person Erik van Velzen    schedule 05.07.2019