This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2023-02-09
Channels
- # announcements (26)
- # babashka (4)
- # beginners (17)
- # calva (21)
- # cider (13)
- # clerk (17)
- # clj-commons (23)
- # clj-kondo (3)
- # cljdoc (47)
- # cljsrn (10)
- # clojure (123)
- # clojure-belgium (2)
- # clojure-dev (25)
- # clojure-europe (34)
- # clojure-gamedev (2)
- # clojure-italy (1)
- # clojure-nl (3)
- # clojure-norway (4)
- # clojure-uk (4)
- # clojurescript (86)
- # cursive (12)
- # datahike (2)
- # datomic (2)
- # emacs (4)
- # fulcro (6)
- # funcool (15)
- # instaparse (1)
- # integrant (11)
- # jobs (1)
- # joyride (9)
- # kaocha (3)
- # membrane (8)
- # off-topic (1)
- # pathom (4)
- # practicalli (2)
- # quil (1)
- # rdf (1)
- # reagent (9)
- # remote-jobs (1)
- # shadow-cljs (27)
- # spacemacs (4)
- # specter (1)
- # sql (11)
- # tools-deps (55)
- # vim (1)
Hello! I was wondering if folks have any intuitive patterns within clojure for when they find themselves running consecutive maps on data? For example, I am trying to get some deeply nested data, from an object something like this:
[[:a [:foo "y" :matches [[:bar "x" :matches [:baz "b" :line "this is what i want"]]]]
[:b [...same structure]]
what I’d like to do is get a list of all the line values across this list (in practice it’s thousands of entries in this array)
My first pass came from the repl, where I just continually added maps to get more precise to the value I wanted, and so it ended up looking like this:
(def image-lines (apply concat (map #(map :Line (flatten %)) (map #(map :Matches %) (map #(:Matches (second %)) (:Results hound))))))
Which feels messy, but I was just sorta curious if there’s moments when y’all are working in the repl and get into forms like this and can immediately see it needs a different pattern?It's kind of hard to tell what you're trying to do. The data format from your example looks really awkward. I might try to normalize it or even put it in a database. Having said that, I often use a library called https://grishaev.me/en/zippo/ for generic search. You can use it something like:
(require
'[clojure.zip :as z]
'[zippo.core :as zippo])
(def data
[[:a [:foo "y" :matches [[:bar "x" :matches [:baz "b" :line "this is what i want"]]]]]
[:b [:foo "y" :matches [[:bar "x" :matches [:baz "b" :line "this is what i want"]]]]]])
(->> (zippo/loc-find-all
(z/vector-zip data)
;; find all the :line keywords
(zippo/->loc-pred #(= :line %)))
;; navigate to the next element in the structure
(map (fn [zv]
(z/next zv)))
;; unwrap the data
(map z/node))
;; ("this is what i want" "this is what i want")
This just general info. I have not delved into your example at all to see if fit fits your specific case:
If you ignore the automatic coercion of Seqable -> Seq (and of course follow the practice of not injecting side-effects), then map
holds the property of being associative.
This means that, in general, you can always replace mapping fn f
over the result of mapping g
with instead just mapping the composition of f
and g
. All the following should return the same output, with the number of traversals required being the only meaningful difference.
; 3 traversals
(map f (map g (map h m)))
; 3 traversals
(->> m (map h) (map g) (map f)
; 2 traverals
(map f (map (comp g h) m))
; 2 traversals
(map (comp f g) (map h m))
; 1 traversal
(map (comp f g h) m)
; 1 traversal
(map #(-> % h g f) m)
So the 3 traversal case may result in more allocations, but is still O(count(m)) not 0(count(m)*3)
Regardless, upon full realization, there should be no difference in the final results of any above. 😉
clojure.walk is helpful for when you know the shape of the data fragment you care about working on, but don't know/care where it is in your data
I would also look into: https://github.com/redplanetlabs/specter . There are some great youtube videos on it showing how it works in practice.
And if you are looking to reshape your data so that you can put it in a database or use a dataset (from http://Tech.Ml) this is a great library as well: https://github.com/turtlegrammar/faconne
Is there idiomatic way to handle blocking deref
in go
block? I have some async API requests that return as promise
and deref
it directly in go
block would block the thread pool. Should I change the promise
to promise-chan
?