tags:

views:

94

answers:

3

Given:

(def my-vec [{:a "foo" :b 10} {:a "bar" :b 13} {:a "baz" :b 7}])

How could iterate over each element to print that element's :a and the sum of all :b's to that point? That is:

"foo" 10
"bar" 23
"baz" 30

I'm trying things like this to no avail:

; Does not work!    
(map #(prn (:a %2) %1) (iterate #(+ (:b %2) %1) 0)) my-vec) 

This doesn't work because the "iterate" lazy-seq can't refer to the current element in my-vec (as far as I can tell).

TIA! Sean

+1  A: 

You could look at this as starting with a sequence of maps, filtering out a sequence of the :a values and a separate sequence of the rolling sum of the :b values and then mapping a function of two arguments onto the two derived sequences.

create sequence of just the :a and :b values with

(map :a my-vec)
(map :b my-vec)

then a function to get the rolling sum:

 (defn sums [sum seq]
   "produce a seq of the rolling sum"
   (if (empty? seq) 
      sum
      (lazy-seq
        (cons sum
              (recur (+ sum (first seq)) (rest seq))))))

then put them together:

(map #(prn %1 %s) (map :a my-vec) (sums 0 (map :b my-vec))) 

This separates the problem of generating the data from processing it. Hopefully this makes life easier.

PS: whats a better way of getting the rolling sum?

Arthur Ulfeldt
Answering the PS: try `clojure.contrib.seq/reductions`.
Michał Marczyk
Thank you, Arthur. I like the idea of separating the data.
scrotty
+4  A: 
user> (reduce (fn [total {:keys [a b]}]
                  (let [total (+ total b)]
                    (prn a total)
                    total))
              0 my-vec)
"foo" 10
"bar" 23
"baz" 30
30
Brian Carper
Super elegant. Thanks Brian! :)
scrotty
A: 

Transform it into the summed sequence:

(defn f [start mapvec]
   (if (empty? mapvec) '()
       (let [[ m & tail ] mapvec]
          (cons [(m :a)(+ start (m :b))] (f (+ start (m :b)) tail)))))

Called as:

(f 0 my-vec)

returns:

(["foo" 10] ["bar" 23] ["baz" 30])
John Lawrence Aspden