Message Boards Message Boards

0
|
3501 Views
|
2 Replies
|
3 Total Likes
View groups...
Share
Share this post:

Unfolding a nested association

I'm embarrassed by my code because there has to be a better way to do it. Here's a hierarchical list of lab tests Category --> Class --> Test --> Status

<|"Asphaltene" -> <|"Flocculation Point Apparatus" -> <|"FPA" -> 
      "Not started"|>, 
   "Miscellaneous Asphaltenes" -> <|"Asphaltene Content (Heptane \
Insolubles)" -> "Not started"|>|>, 
 "General Characterization" -> <|"Chemical Stability" -> <|"Long Term \
HT Stability" -> "Not started", 
     "Long Term Low Temp Stability" -> "Not started"|>, 
   "Pour Point" -> <|"Pour Point" -> "Not started"|>|>|>

I want to turn it into this:

Asphaltene - Flocculation Point Apparatus - FPA
Asphaltene - Miscellaneous Asphaltenes - Asphaltene Content (Heptane Insolubles)
General Characterization - Chemical Stability - Long Term HT Stability
General Characterization - Chemical Stability - Long Term Low Temp Stability
General Characterization - Pour Point - Pour Point

There has to be a shortcut I'm not thinking of so consider this a fun coding challenge to write the shortest form of the following function:

(*A function to unfold an association*)
unfoldAssociation[association_] := 
     KeyValueMap[With[{key = #1, val = #2},
        If[
         ListQ[val],
         Function[v, <|key -> v|>] /@ val,
         <|key -> val|>]
        ] &, association]
(*Map it at each level*)
unfold3LevelAssociation[association_]:=Block[{temp},
  temp=Map[
    unfoldAssociation,association,
    {2}];
  temp=Flatten/@Map[
    unfoldAssociation,temp,
    {1}];
  temp=FixedPoint[Normal,unfoldAssociation[temp]/.Rule->List//Flatten]/.Rule->List;
  Partition[Flatten[temp],4]
]
(*apply the unfold3layer function and do some formatting - drop the last column*)
unfold3LevelAssociation[lProjectList[[1]]["Test Statuses"]];
StringRiffle[#, " - "] & /@ %[[;; , ;; 3]] // TableForm

It works but it's ugly. What magic function does this in one line?

POSTED BY: Eric Smith
2 Replies
Posted 5 years ago

Eric,

Take a look at this. A really elegant solution.

associationFlatten[assoc_Association] := 
 Association[Normal[KeyMap[List, assoc]] //. (n_ -> m_Association) :> 
  Normal[KeyMap[Append[n, #] &, m]]]

Use it on your example

assoc = <|"Asphaltene" -> <|
    "Flocculation Point Apparatus" -> <|"FPA" -> "Not started"|>, 
    "Miscellaneous Asphaltenes" -> <|
      "Asphaltene Content (Heptane Insolubles)" -> "Not started"|>|>, 
  "General Characterization" -> <|
    "Chemical Stability" -> <|
      "Long Term HT Stability" -> "Not started", 
      "Long Term Low Temp Stability" -> "Not started"|>, 
    "Pour Point" -> <|"Pour Point" -> "Not started"|>|>|>


associationFlatten[assoc] // Keys // Map[StringRiffle[#, " - "] &] // Column
(*
Asphaltene - Flocculation Point Apparatus - FPA
Asphaltene - Miscellaneous Asphaltenes - Asphaltene Content (Heptane Insolubles)
General Characterization - Chemical Stability - Long Term HT Stability
General Characterization - Chemical Stability - Long Term Low Temp Stability
General Characterization - Pour Point - Pour Point
*)

Or using Alan Calvitti's "magic bullet"

associationFlatten[assoc] // Keys // Map[StringRiffle[\[Bullet], " - "]] // Column
POSTED BY: Rohit Namjoshi

Nice! Thanks for the link and the solution.

I was convinced there was some trick with ListCorrelate or some esoteric function that I didn't know about. I guess the tried and true pattern matching is the answer.

POSTED BY: Eric Smith
Reply to this discussion
Community posts can be styled and formatted using the Markdown syntax.
Reply Preview
Attachments
Remove
or Discard

Group Abstract Group Abstract