Association of Associations : how to permute level 1 and level 2 keys?
Mathematica 10.1 almost supports this operation directly:
assoc // Query[Transpose]
(*
<| "a" -> <|"1" -> "x", "2" -> Missing["KeyAbsent", "a"]|>,
"b" -> <|"1" -> "y", "2" -> "z"|>,
"c" -> <|"1" -> Missing["KeyAbsent", "c"], "2" -> "k"|>
|>
*)
All that remains is to delete the unwanted Missing
elements:
assoc // Query[Transpose] // DeleteMissing[#, 2]&
(*
<| "a" -> <|"1" -> "x"|>,
"b" -> <|"1" -> "y", "2" -> "z"|>,
"c" -> <|"2" -> "k"|>
|>
*)
We can see that Query
uses the undocumented function GeneralUtilities`AssociationTranspose
to do the heavy-lifting:
Query[Transpose] // Normal
(* GeneralUtilities`AssociationTranspose *)
assoc // GeneralUtilities`AssociationTranspose
(*
<| "a" -> <|"1" -> "x", "2" -> Missing["KeyAbsent", "a"]|>,
"b" -> <|"1" -> "y", "2" -> "z"|>,
"c" -> <|"1" -> Missing["KeyAbsent", "c"], "2" -> "k"|>
|>
*)
An Imperative Solution
The words "elegant" and "imperative" rarely appear together these days, but an imperative solution can express the transposition directly:
Module[{r = <| |>}
, Do[r = Merge[{r, <| j -> <| i -> assoc[[i, j]] |> |>}, Association]
, {i, Keys[assoc]}
, {j, Keys[assoc[[i]]]}
]
; r
]
(*
<| "a" -> <|"1" -> "x"|>,
"b" -> <|"1" -> "y", "2" -> "z"|>,
"c" -> <|"2" -> "k"|>
|>
*)
A ScanIndexed operator would come in handy here (the undocumented one in GeneralUtilities`
is not, well, general enough).
Not as nice as what WReach posted but this is what I came up with:
GroupBy[
Join @@ Thread /@ Normal //@ assoc,
{First@*Last, First}
][[All, All, 1, 2, 2]]
<|"a" -> <|"1" -> "x"|>, "b" -> <|"1" -> "y", "2" -> "z"|>, "c" -> <|"2" -> "k"|>|>
Here is another version, using KeyValueMap
(needs 10.1):
regroup =
Composition[
Map[Association],
GroupBy[First -> Last],
Flatten,
Normal,
KeyValueMap[Function[{k, v}, Map[k -> # &, v]]]
];
So that
regroup @ assoc
(* <|"a" -> <|"1" -> "x"|>, "b" -> <|"1" -> "y", "2" -> "z"|>, "c" -> <|"2" -> "k"|>|> *)