Please consider the reader when writing code! There are no prizes for playing "code golf". There are, however, considerable costs to others when you force them to decipher code that is overly condensed.
I always try to be explicit about what code is doing. This is easiest if you break down a problem into simple steps and use good names. In particular, it is almost impossible to accomplish this using juxt or any other cryptic function.
Here is how I would implement the solution:
(def data [{:id 1 :first-name "John1" :last-name "Dow1" :age "14"} {:id 2 :first-name "John2" :last-name "Dow2" :age "54"} {:id 3 :first-name "John3" :last-name "Dow3" :age "34"} {:id 4 :first-name "John4" :last-name "Dow4" :age "12"} {:id 5 :first-name "John5" :last-name "Dow5" :age "24"}]) (def data-keys (keys (first data))) (defn create-empty-result "init result map with an empty vec for each key" [data] (zipmap data-keys (repeat []))) (defn append-map-to-result [cum-map item-map] (reduce (fn [result map-entry] (let [[curr-key curr-val] map-entry] (update-in result [curr-key] conj curr-val))) cum-map item-map)) (defn transform-data [data] (reduce (fn [cum-result curr-map] (append-map-to-result cum-result curr-map)) (create-empty-result data) data))
with results:
(dotest (is= (create-empty-result data) {:id [], :first-name [], :last-name [], :age []}) (is= (append-map-to-result (create-empty-result data) {:id 1 :first-name "John1" :last-name "Dow1" :age "14"}) {:id [1], :first-name ["John1"], :last-name ["Dow1"], :age ["14"]}) (is= (transform-data data) {:id [1 2 3 4 5], :first-name ["John1" "John2" "John3" "John4" "John5"], :last-name ["Dow1" "Dow2" "Dow3" "Dow4" "Dow5"], :age ["14" "54" "34" "12" "24"]}))
Note that I included unit tests for the helper functions as a way of both documenting what they are intended to do as well as demonstrating to the reader that they actually work as advertised.
This template project can be used to run the above code.