Wes Reimer
Wes Reimer

Reputation: 308

Convert map of vectors to vectors of columns in Clojure

I have a collection (or list or sequence or vector) of maps like so:

{ :name "Bob", :data [32 11 180] }
{ :name "Joe", :data [ 4  8  30] }
{ :name "Sue", :data [10  9  40] }

I want to create new vectors containing the data in the vector "columns" associated with keys that describe the data, like so:

{ :ages [32 4 10], :shoe-sizes [11 8 9], :weights [180 30 40] }

Actually, a simple list of vectors might be adequate, i.e.:

[32 4 10] [11 8 9] [180 30 40]  

If it's better/easier to make the original list into a vector, that's fine; whatever's simplest.

Upvotes: 2

Views: 396

Answers (2)

Jarlax
Jarlax

Reputation: 1576

Given

(def records
  [{:name "Bob" :data [32 11 180]}
   {:name "Joe" :data [ 4  8  30]}
   {:name "Sue" :data [10  9  40]}])

you could do next transformations to get the desired result:

(->> records
     (map :data) ; extract :data vectors 
     ; => ([32 11 180] [4 8 30] [10 9 40])
     (apply map vector) ; transpose
     ; => ([32 4 10] [11 8 9] [180 30 40])
     (zipmap [:ages :shoe-sizes :weights])) ; make map 
     ; => {:weights [180 30 40], :shoe-sizes [11 8 9], :ages [32 4 10]}

Without comments it looks a little bit cleaner:

(->> records 
     (map :data)
     (apply map vector)
     (zipmap [:ages :shoe-sizes :weights]))

Without threading macro it is equivalent to more verbose:

(let [extracted (map :data records)
      transposed (apply map vector extracted)
      result (zipmap [:ages :shoe-sizes :weights] transposed)]
  result)

Upvotes: 4

ebaxt
ebaxt

Reputation: 8417

You could use reduce like this:

(def data [{ :name "Bob", :data [32 11 180] }
           { :name "Joe", :data [ 4  8  30] }
           { :name "Sue", :data [10  9  40] }])

(reduce 
  (fn [acc {[age shoe-size weight] :data}]
    (-> acc
      (update-in [:ages] conj age)
      (update-in [:shoe-sizes] conj shoe-size)
      (update-in [:weights] conj weight)))
{}
data)

Returns something like this:

{:weights (40 30 180), :shoe-sizes (9 8 11), :ages (10 4 32)}

I think the most interesting part of this code is the use of nested destructuring to grab hold of the keys: {[age shoe-size weight] :data}

Upvotes: 1

Related Questions