Given a data structure, I'd like to re-structure it to be grouped by one of the nested values. These values are vectors, and whenever I encounter more than one value, I'm getting stuck.
Given a vector of maps like this:
(def tools
[{:name "A",
:config
{:accepts ["id"],
:classes ["X"]}}
{:name "B",
:config
{:accepts ["id"],
:classes ["X", "Y"]
}}])
I can almost get what I want - the values sorted by "classes" as a key, with values repeated if need be - by running group-by
:
(group-by #(get-in % [:config :classes]) tools)
But it takes the whole vector in :classes
as the key.
{["X"] [{:name "A",
:config {:accepts ["id"],
:classes ["X"]}}],
["X" "Y"] [{:name "B",
:config {:accepts ["id"],
:classes ["X" "Y"]}}]}
What I really want is to copy the values once per class, to look like this:
{"X" [{:name "A"
:config {:accepts ["id"]
:classes ["X"]}}
{:name "B"
:config {:accepts ["id"]
:classes ["X" "Y"]}}]
"Y" [{:name "B"
:config {:accepts ["id"]
:classes ["X" "Y"]}}]}
I'm not quite sure how to handle this given that I have multiple values in classes
.
Working repl demo: https://repl.it/@YoYehudi/FamiliarDisguisedXiphiasgladius
Here's a way to do it using a nested reduce
:
(defn aggregate-classes [m tool]
(->> (get-in tool [:config :classes])
(reduce (fn [acc elem]
(update acc elem conj tool))
m)))
(reduce aggregate-classes {} tools)
=>
{"X" ({:name "B", :config {:accepts ["id"], :classes ["X" "Y"]}} {:name "A", :config {:accepts ["id"], :classes ["X"]}}),
"Y" ({:name "B", :config {:accepts ["id"], :classes ["X" "Y"]}})}