Fork me on GitHub
#specter
<
2016-07-15
>
rui.yang12:07:52

Hi, wonder if specter could do the following:

rui.yang12:07:35

combine the element in the following list based on item_id: [{:id -1, :item_id "2", :quantity "3"} {:id -2, :item_id "1", :quantity "2”} {:id -3, :item_id “1”, :quantity “3"]

rui.yang12:07:58

it will be transform to [{:id -1, :item_id "2", :quantity "3"} {:id -2, :item_id "1", :quantity “5”}]

rui.yang12:07:24

or what is the best way of doing it in clojure?

Chris O’Donnell12:07:29

@rui.yang: I don't think this is something that you particularly need specter for. You should be able to accumulate your result using reduce.

rui.yang12:07:29

@codonnell: thanks for the tips

rui.yang12:07:19

seems a good usage for reduce, I didn’t think of it 🙂

Chris O’Donnell12:07:55

If you want, you could omit the vals and leave the result as a map of items, keyed by item_id

Chris O’Donnell12:07:14

This is a more natural way to store items, in my opinion.

rui.yang12:07:44

yes, but I am using that to map to a list of in UI

rui.yang12:07:04

the id of every row in UI is actually :id key

rui.yang12:07:20

(def items [{:id -1, :item_id "2", :quantity "3"} {:id -2, :item_id "1", :quantity "2”} {:id -3, :item_id “1”, :quantity “3"}])

rui.yang12:07:46

(reduce (fn [m {:keys [item_id quantity] :as item}] (if (contains? m item_id) (update-in m [item_id :quantity] #(+ % quantity)) (assoc m item_id item))) {} items)

rui.yang12:07:29

{"2" {:id -1, :item_id "2", :quantity "3"}, "1" {:id -2, :item_id "1", :quantity "2”} {:id -3, :item_id “1”, :quantity “3"}}

Chris O’Donnell12:07:30

You need to parse the :quantity values to integers.

rui.yang12:07:49

yes, I need to do that

rui.yang12:07:53

but look at that map

rui.yang12:07:05

the value of “1” is odd

rui.yang12:07:46

I tried in both clojure and clojurescript, same result

Chris O’Donnell12:07:55

You're using nonstandard quote characters around the quantity key in your second item, which is causing the problem, I think.

Chris O’Donnell12:07:37

=> (count items)
2

Chris O’Donnell12:07:01

it thinks the value for :quantity in the second item is just a long string

madstap12:07:34

Yeah, copy pasting into my emacs shows the problem in the syntax hinghlighter

rui.yang12:07:47

yes, hard to spot

madstap12:07:02

If you fix it you'll get a class cast exception

rui.yang12:07:10

a special quote character

rui.yang12:07:20

thank you all, save me tons of pain @madstap @codonnell

madstap12:07:24

I fixed it like this:

(def items [{:id -1, :item_id "2", :quantity "3"}
            {:id -2, :item_id "1", :quantity "2"}
            {:id -3, :item_id "1", :quantity "3"}])

(defn str->int [s]
  (Integer/parseInt s))

(reduce (fn [m {:keys [item_id quantity] :as item}]
                (if (contains? m item_id)
                  (update-in m [item_id :quantity] #(+ % quantity))
                  (assoc m item_id item)))
            {}
            (map #(update % :quantity str->int) items))

rui.yang13:07:59

it shouldn’t be string, it should be integer. but when UI form update values, somehow it is setting String. I think I should fix it there instead of fixing it in the transformation of map.

Chris O’Donnell13:07:09

@rui.yang: On a side note, you could use specter to parse those :quantity keys into integers, though it's pretty easy to do in core, as well.

=> (transform [ALL :quantity] #(Integer. %) items)
[{:id -1, :item_id "2", :quantity 3} {:id -2, :item_id "1", :quantity 2} {:id -3, :item_id "1", :quantity 3}]

rui.yang13:07:10

@codonnell: thanks for the tip