isamendonca
isamendonca

Reputation: 59

Update vector inside reduce

Given a vector:

(def vec [{:key 1, :value 10, :other "bla"}, {:key 2, :value 13, :other "bla"}, {:key 1, :value 7, :other "bla"}])

I'd like to iterate over each element and update :value with the sum of all :values to that point, so I would have:

[{:key 1, :value 10, :other "bla"}, {:key 2, :value 23, :other "bla"}, {:key 1, :value 30, :other "bla"}])

I've found this for printing the result, but I've tried to change the prn command to update-in, assoc-in in the code below (extracted from the link above) but I didn't work quite well.

(reduce (fn [total {:keys [key value]}]
                  (let [total (+ total value)]
                    (prn key total)
                    total))
              0 vec)

I'm new to Clojure, how can I make it work?

Upvotes: 1

Views: 205

Answers (2)

Thumbnail
Thumbnail

Reputation: 13473

You can accumulate the :values thus:

(reductions + (map :value v))
=> (10 23 30)

(I renamed the vector v to avoid tripping over clojure.core/vec.)

Then you can use mapv over assoc:

(let [value-sums (reductions + (map :value v))]
  (mapv #(assoc %1 :value %2) v value-sums))
=> [{:key 1, :value 10, :other "bla"} {:key 2, :value 23, :other "bla"} {:key 1, :value 30, :other "bla"}]

Upvotes: 1

Chris Murphy
Chris Murphy

Reputation: 6509

If you want to get the running totals then the simplest way is to use reductions:

(reductions (fn [acc ele] (+ acc (:value ele))) 
            0
            [{:key 1, :value 10, :other "bla"}, {:key 2, :value 13, :other "bla"}, {:key 1, :value 7, :other "bla"}])
;; => (0 10 23 30)

As you can see the function you pass to reductions has the same signature as the function you pass to a reduce. It is like you are asking for a reduce to be done every time a new element is reached. Another way of thinking about it is that every time a new accumulator is calculated it is kept, unlike with reduce where the caller only gets to see the result of the last calculation.

And so this is the code that would directly answer your question:

(->> [{:key 1, :value 10, :other "bla"}, {:key 2, :value 13, :other "bla"}, {:key 1, :value 7, :other "bla"}]
     (reductions #(update %2 :value + (:value %1))
                 {:value 0})
     next
     vec)
;; => [{:key 1, :value 10, :other "bla"} {:key 2, :value 23, :other "bla"} {:key 1, :value 30, :other "bla"}]

Upvotes: 2

Related Questions