I was helping someone on SO with an assignment to create a macro that accepts an SQL-like syntax, and optionally sorts and filters a collection. I realized that while their instructor is insisting on them using macros, macros aren't actually required at all. I decided to try my hand at creating a function that solves the assignment (roughly).
This is my first stab at it. It's not great yet (there's no way to pick between ascending and descending sort order for example), but it's a first step.
An example of its usage:
(def test-db [{:id 1, :name "A"},
{:id 0, :name "B"},
{:id 3, :name "C"},
{:id 2, :name "D"},
{:id 4, :name "E"}])
(select [:name] :from test-db :where [:id < 3] :order-by :id)
=> ("B" "A" "D")
Considering this only took me like 20 minutes to write, I'm pretty happy with it. I do have a few major concerns though:
I dislike how I'm turning the variadic parameters into a map. Right now, I'm using
(let [arg-map (into {} (map vec (partition 2 args))) ...
But this seems overly complicated. It doesn't seem like lazy-seqs can be treated as MapEntries like vectors can, so
(map vec ...
is necessary to allow the conversion. A better way here would be appreciated.Originally I had
:from
as part ofargs
, but it's a necessary field, so it seemed messy to allow it to be optional, only to check for it's existence in the function. I made it a required argument, but that made it so the second argument toselect
is entirely useless and discarded, which also seems messy.Any other suggestions would be appreciated.
(defn- where
"Turns a vector of [key comparator compare-value] into a predicate that filter can use."
[arg-vec]
(let [[k f comp-arg] arg-vec]
(fn [elem] (f (k elem) comp-arg))))
(defn select
"Selects the given keys from the \"database\", filtered by the :where clause, and ordered by the :order-by key.
Example:
(select [:id] :from [{:id 1} {:id 2}] :where [:id > 0] :order-by :id)"
[selection _ from & args]
(let [arg-map (into {} (map vec (partition 2 args)))
{where-vec :where, order-key :order-by} arg-map
filt-ordered
(cond->> from
where-vec (filter (where where-vec))
order-key (sort-by order-key))]
(mapcat #(map % filt-ordered) selection)))
(map vec ...)
you can(map (fn [[k v]] [k v]))
. \$\endgroup\$(map (fn [[k v]] [k v]) (partition 2 ...)
? \$\endgroup\$(into {} ...)
, AFAIK. So(->> x (partition 2) (map (fn [[k v]] [k v]) (into {})))
. \$\endgroup\$