Paul C
Paul C

Reputation: 8497

Conditionally rename keys in a list and convert into a map?

Is there a simple way to convert a list of key-values into a map while also renaming the keys in a conditional way?

Example:

 [{:count 4, :happened "true"} {:count 1, :happened "false"}]

converted into:

{:happened-count: 4, :didnt-happen-count: 1}

I'm kinda close:

user=> (def foo [{:count 4, :happened "true"} {:count 1, :happened "false"}])

user=> (into {} (map (juxt :happened :count) foo))
{"true" 4, "false" 1}

edit: This works, but it is ugly. Hoping for something nicer:

(clojure.set/rename-keys (into {} (map (juxt :happened :count) foo)) {"true" :happened-count "false" :didnt-happen-count})

Upvotes: 0

Views: 132

Answers (3)

Alan Thompson
Alan Thompson

Reputation: 29976

There are a million ways to approach a problem like this. I like to be explicit about the steps. Here is how I would think about it:

(ns tst.demo.core
  (:use demo.core tupelo.core tupelo.test))

(dotest
  (let [data       [{:count 4, :happened "true"}
                    {:count 1, :happened "false"}
                    {:count 5, :happened "true"}
                    {:count 3, :happened "false"}]
        data-split (group-by :happened data)
        sum-count  (fn [recs]
                     (reduce +
                       (mapv :count recs)))
        result     {:happened-num     (sum-count (get data-split "true"))
                    :didnt-happen-num (sum-count (get data-split "false"))}]
    (is= data-split
      {"true"  [{:count 4, :happened "true"}
                {:count 5, :happened "true"}],
       "false" [{:count 1, :happened "false"}
                {:count 3, :happened "false"}]} )
    (is= result {:happened-num 9, :didnt-happen-num 4})))

I added multiple records for the true & false cases, as I that seems like a more typical use case.

Upvotes: 1

leetwinski
leetwinski

Reputation: 17859

i would rather advice using simple reduction

(def mapping {"true" :happened-count "false" :didnt-happen-count})

(reduce #(assoc % (-> %2 :happened mapping) (:count %2)) {} data)

;;=> {:happened-count 4, :didnt-happen-count 1}

Upvotes: 3

Denis Fuenzalida
Denis Fuenzalida

Reputation: 3346

If it helps, you can use the ->> macro to see the transformation as a pipeline:

(->> [{:count 4, :happened "true"} {:count 1, :happened "false"}]
     (map (juxt :happened :count))
     (into {})
     (clojure.set/rename-keys {"true"  :happened-count
                               "false" :didnt-happen-count}))

eg. first extract the values, then group them into a new map, then rename the keys

Upvotes: 2

Related Questions