Reputation: 308
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
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
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