Sam
Sam

Reputation: 2331

Using clojure filter with a dynamic function passed in

I am defining a function, which takes in an array that is to be reformatted to a function. This returned function is the first argument to a filter command. The second argument is this table which I have defined

(def persons '({:id 1 :name "olleš"} {:id 2 :name "anna"} {:id 3 :name "isak"} {:id 4 :name "beatrice"}))

When I run my filter command

seminar.core=> (filter (fn [word func arg] (func word arg)) [:id > 2] persons)

I receive this error

ArityException Wrong number of args (3) passed to: core/filter  clojure.lang.AFn.throwArity (AFn.java:429)

I would like my filter function to behave similarly to this one below

(filter #(< (:id %) 2) persons)
({:id 1, :name "olle"})

Upvotes: 0

Views: 183

Answers (2)

Alan Thompson
Alan Thompson

Reputation: 29958

For simple stuff, I would just use an anonymous function:

(filter #(> (:id %) 2) persons) 

    => ({:id 3, :name "isak"} {:id 4, :name "beatrice"})

Another solution is to write a predicate-generating function, instead of doing it inline.

(defn build-pred
  [word func arg]
  (fn [item]
    (func (word item) arg)))

(filter (build-pred :id > 2) persons))

  => ({:id 3, :name "isak"} {:id 4, :name "beatrice"})

If you really want to pass in the args as a vector, just use destructuring:

(defn build-pred-vec
  [[word func arg]]
  (fn [item]
    (func (word item) arg)))

(filter (build-pred-vec [:id > 2]) persons)

Upvotes: 1

Charles Duffy
Charles Duffy

Reputation: 295393

This is a job for partial application:

(filter 
  (partial
    (fn [word func arg item] (func (word item) arg))
    :id > 2)
  persons)

Here, we create a partially applied version of the function that has word, func and arg already filled in, and leave another argument -- item -- open for the item being filtered.

Upvotes: 1

Related Questions