11

Considering we have a list of rules:

In[11]:= lst = {a -> {1 -> {A1, A2, A3, A4}, b -> 2}, c -> {3 -> C3, 4 -> C4}}

Out[11]= {a -> {1 -> {A1, A2, A3, A4}, b -> 2}, c -> {3 -> C3, 4 -> C4}}

Trying to make this an association parses only the beginning and the end of the list of rule:

In[13]:= Association@lst

Out[13]= <|a -> {1 -> {A1, A2, A3, A4}, b -> 2}, c -> {3 -> C3, 4 -> C4}|> 

The output I would like to have is:

<|a -> <|1 -> {A1, A2, A3, A4}, b -> 2|>, c -> <|3 -> C3, 4 -> C4|>|>

How can I parse this?

Thanks!

SuTron
  • 1,708
  • 1
  • 11
  • 21

5 Answers5

19

The undocumented function ToAssociations in the GeneralUtilities package does this

In[3]:= Needs["GeneralUtilities`"]

In[4]:= ToAssociations[lst]

(* Out[4]= <|a -> <|1 -> {A1, A2, A3, A4}, b -> 2|>, 
c -> <|3 -> C3, 4 -> C4|>|> *)

As with any undocumented function, use with caution, as contents tend to shift during flight.

Daniel W
  • 3,416
  • 18
  • 31
15

Not pretty but works:

lst //. x : {__Rule} :> Association[x]
Kuba
  • 136,707
  • 13
  • 279
  • 740
6

This works too and it's theoretically faster than ReplaceRepeated (//.):

asc = Replace[list, r : {__Rule} :> Association[r], {0, Infinity}]

The key observation is that Replace starts with the innermost levels first and works its way outwards. In contrast, ReplaceAll and ReplaceRepeated start with the outermost levels and work their way inwards.

Szabolcs
  • 234,956
  • 30
  • 623
  • 1,263
  • It's interesting to me that the reverse does not work, i.e.: Replace[list, r : <|___Rule|> :> List[r], {0, Infinity}] – geordie Nov 26 '18 at 10:41
  • 1
    @geordie Association is atomic. Its innards cannot be matched directly. There are tools like KeyValuePattern to extract information during matching. But what you'd do here is something like a_?AssociationQ :> Normal[a]. – Szabolcs Nov 26 '18 at 11:11
  • Thanks for clarifying. I'm still wrapping my head around Association as a useful construct. Lists of rules seem elegant enough! – geordie Nov 26 '18 at 11:19
  • 1
    @geordie The main reason is performance. Associations will always be faster, often much faster, then lists of rules. There is also a clear and concise syntax for extracting data, which does not exist for rule lists. – Szabolcs Nov 26 '18 at 12:11
0

I tried this with a couple of different level specifications in Map, but it seems once the outer List becomes an Association, the other levels of Map are not used. So, instead, we apply it twice:

Map[Association]@Association@lst
(* <|a -> <|1 -> {A1, A2, A3, A4}, b -> 2|>, c -> <|3 -> C3, 4 -> C4|>|> *)
rcollyer
  • 33,976
  • 7
  • 92
  • 191
0

I played around with the levelspec of Map and apparently this combination works. I have no idea what magic makes this work, though:

Map[Association, lst, {-6, -3}]

Or the following (from @Kuba's simultaneous edit):

Map[Association, lst, {0, -3}]
Gerli
  • 1,051
  • 6
  • 12
  • I tried something similar but it won't work if Values are going to be non atomic: {a -> {1 -> {A1, A2, A3, A4}, b -> {b1 -> 1, b2 -> f[s[g[h]]]}}, c -> {3 -> C3, 4 -> C4}}. – Kuba Apr 09 '15 at 13:22