Как найти путь ключей к значению в карте вложенного массива в Clojure? - PullRequest
3 голосов
/ 21 апреля 2020

Предположим, у меня было:

(def a-map {:foo "bar" :biz {:baz "qux"}})

Как найти путь ключей к заданному значению "qux", чтобы

(get-in a-map <the resulting path>) 

вернул бы "qux"?

Другими словами, функция, которая берет a-map и "qux" и возвращает [: biz: baz].

Тогда я смогу использовать возвращаемый путь следующим образом:

 (get-in a-map [:biz :baz])

и получите "qux".

Пути, которые мне нужны, будут гораздо более вложенными, чем этот простой пример.

Я хочу найти путь к заданному значение в html, которое было проанализировано в карте массива с использованием гикори. Я хочу сделать это, не пытаясь мысленно пройтись по десяткам вложенных ключей / значений. Я открыт для других стратегий.

Ответы [ 2 ]

8 голосов
/ 21 апреля 2020

вы можете использовать молния для этого: например, например:

user> (require '[clojure.zip :as z])
nil

user> 
(loop [curr (z/zipper coll? seq nil a-map)]
  (cond (z/end? curr) nil
        (-> curr z/node (= "qux")) (->> curr
                                        z/path
                                        (filter map-entry?)
                                        (mapv first))
        :else (recur (z/next curr))))
;;=> [:biz :baz]

или то же, но в более «декларативном» стиле:

(some->> a-map
         (z/zipper coll? seq nil)
         (iterate z/next)
         (take-while (complement z/end?))
         (filter #(= (z/node %) "qux"))
         first
         z/path
         (filter map-entry?)
         (mapv first))

обновление

Вы также можете использовать рекурсивный подход classi c:

(defn get-path [endpoint data]
  (cond (= endpoint data) []
        (map? data) (some (fn [[k v]]
                            (when-let [p (get-path endpoint v)]
                              (cons k p)))
                          data)))

user> (get-path "qux" a-map)
;;=> (:biz :baz)
1 голос
/ 22 апреля 2020

Есть 2 способа решить эту проблему с помощью библиотеки Tupelo . Первый использует функцию walk-with-parents-readonly. Когда вы найдете нужный узел, вы сохраняете все родительские узлы, которые можно обработать, чтобы получить необходимую информацию:

(ns tst.demo.core
  (:use demo.core tupelo.core tupelo.test)
  (:require [tupelo.forest :as tf]))

(dotest
  (let [result (atom nil)
        data   {:foo "bar" :biz {:baz "qux"}}]
    (walk-with-parents-readonly data
      {:enter (fn [parents item]
                (when (= item "qux")
                  (reset! result parents)))})
    (is= @result
      [{:foo "bar", :biz {:baz "qux"}}
       [:biz {:baz "qux"}]
       {:type :map-val, :value {:baz "qux"}}
       {:baz "qux"}
       [:baz "qux"]
       {:type :map-val, :value "qux"}]))

Вы также можете использовать библиотека tupelo.forest , который предназначен для обработки HTML и других древовидных структур

  (tf/with-forest (tf/new-forest)
    (let [hiccup      [:foo
                       [:bar
                        [:baz "qux"]]]
          root-hid    (tf/add-tree-hiccup hiccup)
          path-raw    (only (tf/find-paths root-hid [:** :baz]))
          path-pretty (tf/format-path path-raw) ]
      (is= path-pretty
        [{:tag :foo}
         [{:tag :bar}
          [{:tag :baz, :value "qux"}]]]) )))

См. Также пример для извлечения постоянной ссылки из веб-страницы XKCD comi c .

...