Counting items within indexed map

67 Views Asked by At

How do you index and count items within an indexed map? Here's an example - I have example JSON data that looks like this -

[ {
 "id" : "z1",
"content_type" : "single",
"item_1" : "000"
}, {
"id" : "z2",
"content_type" : "double",
"item_1" : "bing",
"item_2" : "bong"
}, {
 "id" : "z3",
"content_type" : "triple",
"item_1" : "tttt",
"item_2" : "toooo",
"item_3": "tiii"
}, {
"id" : "z4",
"content_type" : "double",
"item_1" : "111",
"item_2" : "222"
}]

Going into a component that looks like this -

   [:div.scroller 
      (doall
       (map-indexed
        (fn [index coll]
         (condp = (:content_type coll)
          "single" ^{:key (str index)}[:div (str (:item_1 coll))]
          "double" ^{:key (str index)}[:div 
                                [:div (str (:item_1 coll))][:div (str (:item_2 coll))]
                                       [:div (str index)]]
          )) collections))]

This gives me an index for each grouping, which is needed for ordering and referencing however I would also like a index and count of each individual item within those collections i.e "000" "bing" "bong" etc to be [0 "000"] [1 "bing"] [2 "bong]

1

There are 1 best solutions below

7
Eugene Pakhomov On

You don't need to call str in any of those cases. Also, since your constants are literals, you can replace (condp = ...) with (case ...) (add nil at the end if you don't want it to throw on a missing clause).

As for the question, it seems that you need to restructure the input data so every map has both the content type and exactly one item. You can do so like this:

(into []
      (mapcat (fn [m]
                (let [ct (:content_type m)]
                  (map (fn [v]
                         {:content_type ct
                          :item         v})
                       (vals (dissoc m :id :content_type))))))
      data)

If you need to maintain the grouping but number each item within every group independently of the groups, this is how you can do it:

(defn group-items [m]
  {:id           (:id m)
   :content_type (:content_type m)
   :items        (vec (vals (dissoc m :id :content_type)))})

(defn index-items-by-idx [m offset]
  (update m :items (fn [items]
                     (zipmap (map #(+ offset %) (range))
                             items))))

(let [data [{:id           "z1"
             :content_type "single"
             :item_1       "000"}
            {:id           "z2"
             :content_type "double"
             :item_1       "bing"
             :item_2       "bong"}
            {:id           "z3"
             :content_type "triple"
             :item_1       "tttt"
             :item_2       "toooo"
             :item_3       "tiii"}
            {:id           "z4"
             :content_type "double"
             :item_1       "111"
             :item_2       "222"}]]
  (first (reduce (fn [[acc offset] m]
                   (let [m (-> m
                               (group-items)
                               (index-items-by-idx offset))]
                     [(conj acc m)
                      (+ offset (count (:items m)))]))
                 [[] 0]
                 data)))

=>
[{:id "z1", :content_type "single", :items {0 "000"}}
 {:id "z2", :content_type "double", :items {1 "bing", 2 "bong"}}
 {:id "z3", :content_type "triple", :items {3 "tttt", 4 "toooo", 5 "tiii"}}
 {:id "z4", :content_type "double", :items {6 "111", 7 "222"}}]