ezzato
ezzato

Reputation: 153

How to transform nested vectors into a map of vectors?

Given I have the following form:

(def data-points [[1483249740 "ONE"]
                  [1483249680 "TWO"]
                  [1483249620 "THREE"]
                  [1483249560 "FOUR"]])

How can I transform this data into this?

{:data  [1483249740 1483249680 1483249620 1483249560] 
 :value ["ONE" "TWO" "THREE" "FOUR"]}

I would also like to know how to approach similar problems. What is your way to break this down and what functions do I need to know to transform any data.

I'm new to clojure and haven't found a satisfying solution for this.
Thank you

Upvotes: 2

Views: 366

Answers (4)

leetwinski
leetwinski

Reputation: 17859

i would use this:

(zipmap [:data :value] (apply map vector data-points))

;;=> {:data [1483249740 1483249680 1483249620 1483249560], 
;;    :value ["ONE" "TWO" "THREE" "FOUR"]}

it uses a single pass over the data collections, but more concise than the reduction, yet shouldn't differ in terms of performance

the snippet (apply map vector data) is quite an idiomatic way to transpose matrix in clojure (in your case it is what you need, since it turns columns into rows)

user> (apply map vector [[:a 1] [:b 2] [:c 3]])
;;=> ([:a :b :c] [1 2 3])

Upvotes: 8

adam
adam

Reputation: 1

Here's an obtusely functional way to do it

(->> data-points
     (mapv (partial mapv vector))
     (mapv (partial zipmap [:data :value]))
     (reduce (partial merge-with into)))

As a function:

(def format-data-points
  (comp (partial reduce (partial merge-with into))
        (partial mapv (partial zipmap [:data :value]))
        (partial mapv (partial mapv vector))))

(format-data-points data-points)

(I wouldn't recommend doing either of these actually, just presenting this for fun)

Upvotes: 0

Jordan Biserkov
Jordan Biserkov

Reputation: 691

Another representation which can be both efficient and easier to work with:

(def data-points-map
  (into {} data-points))

Then you can do

(get data-points-map 1483249740)

to get "ONE". Otherwise you would need to

(aget (:value m) (.indexOf (:data m) 1483249740))

to achieve the same result.

Finally you can

{:data (keys data-points-map)
 :value (values data-points-map)}

to get the "weird" representation in the original question.

Upvotes: 0

dpassen
dpassen

Reputation: 1306

I'd probably write this as a reduction. This approach only requires a single pass over 'data-points' which may be preferable.

(reduce
  (fn [m [data value]]
    (-> m
        (update :data conj data)
        (update :values conj value)))
  {:data [] :values []}
  data-points)

Upvotes: 1

Related Questions