Take the 2-minute tour ×
Mathematica Stack Exchange is a question and answer site for users of Mathematica. It's 100% free, no registration required.

The impossibility of MatchQ-ing inside and Association MatchQ-ing Associations (MMA 10) explains a problem I have. I would like to convert hierarchical lists of rules to hierarchies of Associations. As an example:

rdata = {"a" -> {"b" -> 1, "c" -> {7, 8, 9}}, "d" -> 3}

Then

rdata /. List -> Association

returns

<|"a" -> <|"b" -> 1, "c" -> Association[7, 8, 9]|>, "d" -> 3|>

where a value that happens to be a list is converted to something meaningless, which is not wanted. I have tried a variety of things like:

rdata /. x : {__Rule} :> Association[x]

or

(rdata /. List -> Association) /. x_Association /; NotAssociationQ[x] :> List @@ x

or

rdata /. {x__Rule} :> Association[x]

but none of them solves the problem.
I finally managed it by picking out the locations of all the lists of Rules, as follows:

rtoA = (# /. {x__Rule} -> Association[x]) &
RulesToAssociation[rdata_] := Module[{pos},
  pos = Drop[#, -1] & /@ Position[rdata, Rule];
  Association @@ MapAt[rtoA, rdata, pos]]
RulesToAssociation[rdata]

which returns the desired result

<|"a" -> <|"b" -> 1, "c" -> {7, 8, 9}|>, "d" -> 3|>

In my real application the rules list comes from the Import of a fairly complicated JSON file. This solution seems to work but I'm not sure how robust it is. Does anyone have a better idea?

share|improve this question

1 Answer 1

There is a ToAssociations function in the GeneralUtilities` package that is perfect for this and for converting nested JSON rules to associations:

Needs["GeneralUtilities`"]
ToAssociations@rdata
(* <|"a" -> <|"b" -> 1, "c" -> {7, 8, 9}|>, "d" -> 3|> *)

This preserves the inner most list that is not a list of rules.


As for your original attempts, the reason a replacement like x : {__Rule} :> Association[x] does not work is because Association is atomic and when /. replaces the outer most list with Association, you lose the ability to "inspect" inside and make replacements.

Instead, what you want is something that replaces inside out, and you can use Replace with the appropriate level spec:

Replace[rdata, x : {__Rule} :> Association[x], {0, Infinity}]
(* <|"a" -> <|"b" -> 1, "c" -> {7, 8, 9}|>, "d" -> 3|> *)
share|improve this answer
    
Nice job. You gave the answer I was going to give and showed me a new function. :-) –  Mr.Wizard Jul 24 at 13:17
    
Thanks a lot. I was trying to figure out how to get Replace to work like that but I was thinking in terms of the /. syntax and it never occurred to me that it would have level specs. There seems to be no mention of the GeneralUtilities` package in the Help Browser, as far as I can tell ? –  John Jowett Jul 25 at 7:30
    
@JohnJowett Yes, it is undocumented at present. –  rm -rf Jul 25 at 10:21

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.