specter

Latest version: 1.1.3
idiomancy 2018-07-13T00:10:05.000124Z

hi!

idiomancy 2018-07-13T00:16:43.000168Z

I picked up specter to play with hiccup, and I'm trying to form compound keys by grabbing the keys of sub elements within the data. essentially, given

[[:div.row
  [:div.column {:key "A"}]
  [:div.column {:key "B"}]]
 [:div.row
  [:div.culumn {:key "C"}]
  [:div.column {:key "D"}]]]
I want
[[:div.row {:key "AB"}
  [:div.column {:key "A"}]
  [:div.column {:key "B"}]]
 [:div.row {:key "CD"}
  [:div.culumn {:key "C"}]
  [:div.column {:key "D"}]]]
I'll be working on this myself, but if anyone has any ideas, this is effing crazy to write with handrolled clojure, and it seems like the perfect specter magic trick from what I've seen in the videos

nathanmarz 2018-07-13T00:50:29.000130Z

@idiomancy

(def data
  [[:div.row
    [:div.column {:key "A"}]
    [:div.column {:key "B"}]]
   [:div.row
    [:div.culumn {:key "C"}]
    [:div.column {:key "D"}]]])

(transform
  [ALL
   (collect ALL coll? (nthpath 1) :key)
   (before-index 1)]
  (fn [keys _] {:key (apply str keys)})
  data
  )

idiomancy 2018-07-13T00:50:58.000036Z

oooh, man. No way.

idiomancy 2018-07-13T00:52:12.000041Z

It was gonna take me a while to figure out that "collect" navigator, I think šŸ˜†

idiomancy 2018-07-13T00:52:16.000198Z

that's amazing!

nathanmarz 2018-07-13T00:53:11.000003Z

the cheat sheet is handy for finding navigators you need https://github.com/nathanmarz/specter/wiki/Cheat-Sheet

2
idiomancy 2018-07-13T00:53:13.000129Z

let me spend a couple hours reverse engineering this. thanks, @nathanmarz

denik 2018-07-13T14:48:44.000153Z

I asked this in the #clojure channel and wondering if specter could be a solution: https://clojurians.slack.com/archives/C03S1KBA2/p1531346207000010

denik 2018-07-13T14:50:15.000288Z

the reason clojure.spec doesnā€™t work for this case is because spec tries to conform everything. The ebooks I looked at are highly inconsistent. I think what Iā€™m looking for more like a data matcher, rather than a parser

nathanmarz 2018-07-13T15:03:59.000332Z

@denik don't understand the output you're looking for

nathanmarz 2018-07-13T15:04:38.000128Z

{:class "indent" :content "key lesson 1"} does not match {:class "indent" :content "key lesson 1"}

nathanmarz 2018-07-13T15:04:48.000233Z

and there's no "key lesson 3" in your input

denik 2018-07-13T15:06:19.000427Z

@nathanmarz start at ā€œSTART HEREā€ and match multiple :class fields, in this case indent and indent1 but ignore these items before( and after with the stop condition to )

nathanmarz 2018-07-13T15:07:24.000375Z

and you only want to keep items that match the :filter predicate?

denik 2018-07-13T15:08:26.000363Z

yes, but only when they were found between from and to sequentially

nathanmarz 2018-07-13T15:13:17.000333Z

you can do it with specter like this:

(defn start-index [data]
    ;; fill in
  )

(def end-index
  (end-fn [data start-index]
    ;; fill in
    ))


(select
  [(srange-dynamic start-index end-index)
   ALL
   (selected?
    (multi-path
      #(= "START HERE" (:class %))
      #(= "bl" (:class %))
      ))
   ]
  data)

nathanmarz 2018-07-13T15:13:27.000165Z

but really this is better done with a parser

nathanmarz 2018-07-13T15:14:01.000474Z

I'm not very familiar with what spec is capable of, but this is real easy with monadic parser

denik 2018-07-13T15:15:42.000209Z

thank you!

denik 2018-07-13T15:15:51.000247Z

hereā€™s what that looks like in spec

(require '[clojure.spec.alpha :as s]
         '[clojure.pprint :as pp])

(def ebook
  [{:class "indent" :content "ignore"}
   {:class "indent" :content "ignore"}
   {:class "START HERE" :content "Key Lessons"}
   {:class "indent" :content "ignore"}
   {:class "indent1" :content "ignore"}
   {:class "bl" :content "key lesson 1"}
   {:class "bl" :content "key lesson 2"}
   {:class "indent3" :content "ignore"}])

(s/def
  ::ebook-tree
  (s/and
    (s/cat
     :ignore (s/* (s/and map? #(= "ignore" (:content %))))
     :start (s/and map? #(= (:class %) "START HERE"))
     :body (s/* (s/alt
                 :ignore (s/and map? #(= "ignore" (:content %)))
                 :item (s/and map? #(= "bl" (:class %)))))
     :end (s/and map? #(= "indent3" (:class %))))
    (s/conformer
     (fn [{:keys [start body]}]
       (assoc start :children (for [[tag item] body
                                    :when (not= tag :ignore)]
                                item))))))

(pp/pprint
 (s/conform ::ebook-tree ebook))

nathanmarz 2018-07-13T15:17:07.000489Z

with https://github.com/nathanmarz/specter/issues/236 you could do it more easily with specter

denik 2018-07-13T15:46:14.000423Z

@nathanmarz I think thatā€™s exactly what Iā€™m looking for.