0

I have a list of functions which adjust price, and a list of products. As an output, I expect to have the same list of products, but with adjusted prices.

Technically, there is a list of functions and a list of maps. What I'm trying to achieve is to apply each function sequentially to each map in a list, while preserving initial structure

(def products
  [{:id 1 :price 100}
   {:id 2 :price 200}])

(defn change-price
  "increase by 10 => (change-price product 10 +)"
  [product amount price-func]
  (update product :price #(int (price-func amount %))))

(defn adjust-price
  [products]
  ;;fn-list is a list of price adjuster functions
  (let [fn-list [#(change-price % 10 +)
                 #(change-price % 50 -)]]
  ;; so I map a function to each product
  ;; which applies all adjsuter functions to that product
    (merge (map (fn [prod] 
        (map #(% prod) fn-list)) products)))

It seems I don't understand how to reduce the result properly, because what I'm getting is a nested list like

   (change-price products)
=> (({:id 1, :price 110} {:id 1, :price -50})
    ({:id 2, :price 210} {:id 2, :price -150}))

But I expect

({:id 1, :price 60} {:id 2, :price 160})

Thank you in advance.

0

3 Answers 3

2

It seems that you want to apply a composition of your functions:

(defn adjust-price
  [products]
  (let [fn-list [#(change-price % 10 +)
                 #(change-price % 50 -)]
        f (apply comp fn-list)]
    (map f products)))
Sign up to request clarification or add additional context in comments.

2 Comments

Thank you; this is also a completely valid solution.
take a note, that while this solution works ok in this specific case, it would apply functions right to left (which is ok, since x + 10 - 50 == x - 10 + 50), probably leading to some confusion in cases where the order matters. In this case you probably need (apply comp (reverse fn-list))
1

the thing is map doesn't 'squash' results : it just makes list[n] => list[n].

what you need is reduce, something like this:

user> (let [fn-list [#(change-price % 10 +)
                     #(change-price % 50 -)]]
        (map (fn [p] (reduce #(%2 %1) p fn-list))
             products))

;;=> ({:id 1, :price -60} {:id 2, :price -160})

also you would have to rewrite your change-price function, since it has the wrong number of args here: (price-func amount %) => (price-func % amount)

Comments

1

You aren't mutating the hashmap passed in so when you call two different functions on an item the way you are, you will get to separate results.

In the 'adjust price' function, since you are using 'map' to go through the 'change price' functions, you are currently saying, run the first change price function once, return a value, then run the second price function once, return a separate value resulting in: ({:id 1, :price 110} {:id 1, :price -50})

The above answer is good, just thought I'd add another way to do it using a threaded function so that you don't have to worry about order.

(defn adjust-price
  [products]
  (let [f #(-> % 
               (change-price 10 +)
               (change-price 50 -))]
    (map f products)))

remember, single thread '->' means that you are passing the result of the current line(function) down to the next line(function), and it will be used as the first parameter

(ps. I know this is an old post, but hopefully this help someone else in the future:)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.