This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2016-06-08
Channels
- # admin-announcements (3)
- # arachne (1)
- # aws (2)
- # beginners (10)
- # boot (287)
- # cider (5)
- # clara (2)
- # cljs-dev (150)
- # cljsjs (2)
- # clojure (99)
- # clojure-austin (1)
- # clojure-brasil (1)
- # clojure-dev (13)
- # clojure-greece (55)
- # clojure-japan (1)
- # clojure-nl (2)
- # clojure-russia (24)
- # clojure-spec (184)
- # clojure-taiwan (1)
- # clojure-uk (45)
- # clojurescript (55)
- # clojurex (1)
- # cursive (20)
- # datascript (16)
- # datomic (1)
- # devcards (4)
- # events (10)
- # figwheel (1)
- # funcool (7)
- # hoplon (48)
- # immutant (1)
- # jobs (6)
- # lambdaisland (2)
- # lein-figwheel (19)
- # mount (36)
- # off-topic (37)
- # om (16)
- # om-next (17)
- # onyx (29)
- # planck (53)
- # proton (1)
- # pure-frame (1)
- # re-frame (40)
- # reagent (44)
- # remote-jobs (1)
- # ring (2)
- # robots (2)
- # rum (5)
- # slack-help (4)
- # spacemacs (27)
- # specter (82)
- # test-check (18)
- # test200 (1)
- # untangled (17)
I'm trying to replace my group-by
/`merge-with` logic, any suggestion on how to transform:
{:1000 {:a {:sends 1}}
:2000 {:a {:clicks 1 :opens 1 :sends 1}}
:3000 {:b {:sends 1 :opens 1}}}
into
{:a {:sends 2 :opens 1 :clicks 1}
:b {:sends 1 :opens 1}}
I don't see a way to express that in terms of navigation
here's my current ugly solution:
(->> {:1000 {:a {:sends 1}}
:2000 {:a {:clicks 1 :opens 1 :sends 1}}
:3000 {:b {:sends 1 :opens 1}}}
(select [ALL LAST])
(group-by (comp first keys))
(transform [ALL LAST]
#(select [ALL LAST LAST] %))
(transform [ALL LAST]
(partial apply merge-with +)))
(defn merge-with* [f maps]
(apply merge-with f maps)
)
(merge-with* (fn [& maps] (merge-with* + maps)) (vals data))
maybe you could define a merge-with navigator somehow
like from a sequence of maps you navigate to a sequence of values for any given key
so then you could do
(transform [MERGED-MAPS MERGED-MAPS] sum (vals data))
yea it works
(defnav MERGED-MAPS []
(select* [this structure next-fn]
;;TODO: fill this in
)
(transform* [this structure next-fn]
(apply merge-with (fn [& vals] (next-fn vals)) structure)
))
(defn sum [vals] (reduce + vals))
(def data
{:1000 {:a {:sends 1}}
:2000 {:a {:clicks 1 :opens 1 :sends 1}}
:3000 {:b {:sends 1 :opens 1}}})
(transform [MERGED-MAPS MERGED-MAPS] sum (vals data))
@nathanmarz: magic 😉
I do what I can
oops, had sum
written incorrectly there
now it's fixed
@nathanmarz I agree with your point about OMIT
. My main reasoning for proposing it was that it allows you to control which layer of the path you want to pass NONE
to. For example:
(transform [ALL (if-path ... STAY OMIT) ALL (if-path ... STAY OMIT) :a (if-path ... STAY OMIT) ...] ...)
Is there a more idiomatic way to do this without coupling the navigation and the transformation as tightly?well I think what you want is a single operation that can do multiple transforms
@aengelberg as it stands now transform
does a single transform to all navigated elements
I've thought a bit about this, and basically you could imagine a different transform API that does not take in a transform-fn at the top-level
(multi-transform [ALL (if-path even? (setval-transform 2) (setval-transform NONE))] data)
The question being whether it's worth an exception being made for the "specter abstracts the path from the transformation" idea
that's basically why I've held off on this
and also I've gotten by just chaining multiple transformations
(->> data (transform ...) (transform ...))
But that's less efficient right?
yea, which is why I keep thinking about this 🙂
that said, I don't think places where I do that are bottlenecks for me
Btw I've been thinking about the new select semantics, there may be an opportunity to introduce transducers
Since the continuation is basically a "reducer"
got an example?
(select [ALL] (map inc) [1 2 3])
=>
[2 3 4]
(select-reduce conj [] [ALL] [1 2 3])
just some ideas. the latter would be achieved by storing the reduced value in a cell and repeatedly updating it
that last one would output [1 2 3]?
I'm not following
If I understand the new select semantics properly, the continuation (or "final next-fn") is currently "dump into a vector". I'm proposing letting the user specify an alternative for that continuation
oh I see
well there are different continuations for the different selects
you basically want "traverse" exposed
I just looked up traverse
, didn't realize that was generalizing the continuation internally. Yeah, that's basically what I'm saying
I haven't completely internalized transducers yet
transducer: reducer -> reducer
but basically it's like an aggregator that looks at its input one value at a time
and specter traverse looks at values one at a time
exposing traverse
isn't necessarily related at all to transducers
but you're saying you could just plug in a transducer for that continuation fn
err, my posting two examples above probably made things confusing.
My first example (providing (map inc)
) was supplying a transducer to modify the hardcoded reducer
The second example (providing conj
) was replacing the reducer
You could potentially combine both
Except there isn't much advantage of providing both since the user can just call (my-transducer my-reducer)
and get a reducer to pass in
what if specter could just integrate into the normal transducer api?
(eduction xf (traverse [ALL :a even?] data))
or even (eduction xf (traverse [ALL :a even?]) data)
actually the former probably makes more sense
the latter I think would actually be (sequence (traverse [ALL :a even?]) data)
or (sequence (comp xf (traverse [ALL :a even?])) data)
Would traverse
apply the transformation to each input form?
err select, not transformation
here I'm thinking traverse would return an object that can be reduced
that makes more sense
i.e. implements IReduce
and we wouldn't have to worry about clojure 1.6 compatibility I don't think
although I think I might drop that soon anyway
1.9 is coming out and I'm sick of cljx 😉
I was working on a project last year but got stuck on leiningen issues. the idea was a plugin that would take cljc files and output clj / cljs similar to cljx
that would have helped a lot
the main motivation actually was for instaparse; we don't really want to pull in instaparse-cljs
until a non-deprecated cross-compile solution exists
(that also works with <1.7)
maybe i'll pick it back up soon, the main blocking issue was leiningen's built-in tools.reader was too old (no longer an issue probably)
any idea how many people are still on 1.6?
or below
my previous job was still on 1.6 well into the lifetime of 1.8
just one data point, not sure how realistic it is to continue support for 1.6 especially with cider now on >1.7