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] // DeleteCases[#, _MissingDeleteMissing[#, {2}] &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).