This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2022-03-18
Channels
- # aleph (1)
- # announcements (31)
- # babashka (9)
- # babashka-sci-dev (36)
- # beginners (72)
- # calva (20)
- # clj-kondo (99)
- # cljsrn (1)
- # clojure (77)
- # clojure-europe (33)
- # clojure-nl (4)
- # clojure-norway (12)
- # clojure-uk (4)
- # clojurescript (23)
- # cursive (2)
- # datascript (5)
- # events (1)
- # fulcro (3)
- # honeysql (3)
- # inf-clojure (82)
- # interop (2)
- # kaocha (10)
- # lsp (15)
- # meander (1)
- # missionary (10)
- # off-topic (22)
- # pathom (4)
- # pedestal (3)
- # polylith (20)
- # re-frame (10)
- # react (4)
- # reagent (4)
- # reitit (27)
- # ring-swagger (1)
- # shadow-cljs (34)
- # specter (3)
- # sql (1)
- # testing (5)
- # tools-deps (22)
- # vim (12)
Can one implement argmin/argmax-style functions as a datascript aggregation? I first tried:
(d/q '{:find [(argmax ?salary)]
:with [?player]
:where [[?player :salary ?salary]]}
[["A" :salary 10]
["B" :salary 7]
["C" :salary 7]])
;;=> (["A"]). argmin would return (["B"] ["C"]).
but the :with
value doesn't get passed to custom aggregates, so it's not actually possible to write it this way.I can't do (argmax ?player ?salary)
either, since :with
and :find
clauses can't share vars (and I need the ?player
in :with
clause so I can do other aggregations in the same query. E.g., query might be "what is the average salary across all players and who has the largest?"
I guess the problem lies in the fact that custom aggregate functions can only take 1 variable, not tuples: > The one and only aggregated variable must be the last argument to the function. This was copied from Datomic, not sure why the limitation
I guess you can do this
(require '[datascript.core :as d])
(defn argmin [xs]
(let [min (reduce min (map second xs))]
(filter (fn [[player salary]] (= min salary)) xs)))
(defn argmax [xs]
(let [max (reduce max (map second xs))]
(filter (fn [[player salary]] (= max salary)) xs)))
(d/q '[:find (user/argmin ?tuple) (user/argmax ?tuple)
:where [?player :salary ?salary]
[(vector ?player ?salary) ?tuple]]
[["A" :salary 10]
["B" :salary 7]
["C" :salary 7]])
Thanks! Constructing the tuple manually makes sense. I expanded on the idea and show an example with additional grouping:
(defn arg-f
[f]
(fn [tuples]
(let [v (reduce f (map last tuples))]
(keep (fn [tuple]
(when (= v (last tuple))
(vec (butlast tuple))))
tuples))))
(def argmin (arg-f min))
(def argmax (arg-f max))
(d/q '{:find [?even (user/argmin ?tuple) (user/argmax ?tuple)]
:where [[?player :salary ?salary]
[(even? ?salary) ?even]
[(vector ?player ?salary) ?tuple]]}
[["A" :salary 10]
["B" :salary 7]
["C" :salary 5]
["D" :salary 12]
["E" :salary 12]])
;; => ([true (["A"]) (["D"] ["E"])]
[false (["C"]) (["B"] )])