Reputation: 309
I have a map of users and their favorite bands:
(def data
{
:David {"Tribalistas" 3.0
"Daft Punk" 5.0
"Lorde" 4.0
"Fall Out Boy" 1.0}
:Matt {"Imagine Dragons" 3.0
"Daft Punk" 4.0
"Lorde" 4.0
"Fall Out Boy" 1.0}
:Ben {"Kacey Musgraves" 4.0
"Imagine Dragons" 3.0
"Lorde" 3.0
"Fall Out Boy" 1.0}
} )
and I need to filter the results that have two keys in common, in this case, band1 and band2
(defn common-ratings [band1 band2 ratings]
(filter #(and ((second %) band1) ((second %) band2)) ratings))
(common-ratings "Daft Punk" "Lorde" data) ; should return David and Matt lines
but now, I need to transform the bands in a varargs, I tried to use something like:
apply and...
So I can use the function like this:
(common-ratings "Daft Punk" "Lorde" "Another band" "Another Band2" data)
but It does not work.
Thanks in advance
Upvotes: 0
Views: 148
Reputation: 13473
and
is a macro, so cannot be an argument to a
function, apply
or any other.and
is every?
. Since you have an unknown number of bands, pass them as a collection:
(defn common-ratings [bands ratings]
(filter #(every? (val %) bands) ratings))
... where I've replaced second
with val
to show that we're dealing with map entries.
For example,
(common-ratings ["Daft Punk" "Lorde"] data)
;([:David {"Tribalistas" 3.0, "Daft Punk" 5.0, "Lorde" 4.0, "Fall Out Boy" 1.0}] [:Matt {"Imagine Dragons" 3.0, "Daft Punk" 4.0, "Lorde" 4.0, "Fall Out Boy" 1.0}])
If you want to pass the bands
as individual arguments, put them last to capture them as a rest argument:
(defn common-ratings [ratings & bands]
... )
... which you call like this:
(common-ratings data "Daft Punk" "Lorde")
... with the same effect as before.
Upvotes: 2
Reputation: 1576
One of the options is to wrap and
macro into function:
(defn and-fn [x y] (and x y))
Then you can use reduce
instead of apply
to achieve the same result:
(reduce and-fn [true true true false]) ; => false
For convenience, last reduce can be wrapped into another function:
(defn and-multi [& args]
(reduce #(and %1 %2) true args)) ; initial value is needed for invocation with no args
Now it will behave very similar to normal and
macro:
(and-multi) ; => true
(and-multi false true) ; => false
(apply and-multi [true true true]) ; => true
It will even have same behaviour with the evaluation of args:
(and false (range)) ; => false (despite of second param being infinite sequence)
(and-multi false (range)) ; => false
Upvotes: 0
Reputation: 309
Thank you a lot guys, my final solution is pretty close to @Thumbnail solution:
(defn common-ratings [& bands] (filter #(every? (second %) bands) data))
Upvotes: 0