hi!
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(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
)
oooh, man. No way.
It was gonna take me a while to figure out that "collect" navigator, I think š
that's amazing!
the cheat sheet is handy for finding navigators you need https://github.com/nathanmarz/specter/wiki/Cheat-Sheet
let me spend a couple hours reverse engineering this. thanks, @nathanmarz
I asked this in the #clojure channel and wondering if specter could be a solution: https://clojurians.slack.com/archives/C03S1KBA2/p1531346207000010
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
@denik don't understand the output you're looking for
{:class "indent" :content "key lesson 1"}
does not match {:class "indent" :content "key lesson 1"}
and there's no "key lesson 3"
in your input
@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
)
and you only want to keep items that match the :filter
predicate?
yes, but only when they were found between from
and to
sequentially
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)
but really this is better done with a parser
I'm not very familiar with what spec is capable of, but this is real easy with monadic parser
thank you!
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))
with https://github.com/nathanmarz/specter/issues/236 you could do it more easily with specter
@nathanmarz I think thatās exactly what Iām looking for.