Function Repository Resource:

NestedAssociate

Source Notebook

Append a value in a nested association

Contributed by: Robert Ferguson

ResourceFunction["NestedAssociate"][assoc,{key}val]

gives assoc with keyval associated.

ResourceFunction["NestedAssociate"][assoc,{key1,,keyn}val]

gives assoc with val associated deep in the association using the key sequence key1, …, keyn.

Details and Options

ResourceFunction["NestedAssociate"] associates the key sequence to val at arbitrary depth within assoc.
The form of the specified keys is as in certain built-in functions such as Lookup, and resource functions NestedKeyDrop and NestedLookup in that the outermost Key in Key[key] is stripped. This is different from other built-in functions such as Append and KeyExistsQ where the function interprets the key as it is literally given.
Where a key is a list, it must be given as Key[list].
Ordering of the given association is preserved.
Keys and values must be associated using Rule.
The use of RuleDelayed is not supported, nor should it appear anywhere in the given assoc, as it may cause undesired behaviour.
Unevaluated values in the given association, including nested associations, may be evaluated.

Examples

Basic Examples (5) 

Append a new value to an association at the top level:

In[1]:=
ResourceFunction[
 "NestedAssociate"][<|1 -> "one", 2 -> <|21 -> "two.1", 22 -> "two.2"|>|>, {3} -> "THREE"]
Out[1]=
Image

Associate a new value to an existing key, noting that the order of the association is maintained:

In[2]:=
ResourceFunction[
 "NestedAssociate"][<|1 -> "one", 2 -> <|21 -> "two.1", 22 -> "two.2"|>|>, {1} -> "ONE"]
Out[2]=
Image

Associate a new value to an existing key at the second level:

In[3]:=
ResourceFunction[
 "NestedAssociate"][<|1 -> "one", 2 -> <|21 -> "two.1", 22 -> "two.2"|>|>, {2, 21} -> "TWO.1"]
Out[3]=
Image

New keys may also be appended at nested levels:

In[4]:=
ResourceFunction[
 "NestedAssociate"][<|1 -> "one", 2 -> <|21 -> "two.1", 22 -> "two.2"|>|>, {2, 23} -> "TWO.3"]
Out[4]=
Image

Nested associations will be created at depth according to the given key specification:

In[5]:=
ResourceFunction[
 "NestedAssociate"][<|1 -> "one", 2 -> <|21 -> "two.1", 22 -> "two.2"|>|>, {2, 23, 231, 2311} -> "TWO.311"]
Out[5]=
Image

Scope (1) 

Arbitrary keys are supported:

In[6]:=
(* Evaluate this cell to get the example input *) CloudGet["https://www.wolframcloud.com/obj/3fbdf7de-ef18-4da0-8a86-8f5d271c0a82"]
Out[6]=
Image

Applications (2) 

Starting with an association of contacts, append a new contact as an association at the top level:

In[7]:=
contacts = <|
  "Joan" ->
   <|
    "phone" ->
     <|"home" -> "1234", "mobile" -> "5678"|>,
    "address" ->
     <|"street" -> "2 Acacia Drive", "town" -> "Trumpton"|>|>,
  "Mike" ->
   <|
    "phone" ->
     <|"home" -> "2468", "mobile" -> "1357"|>,
    "address" ->
     <|"street" -> "4 Elm Close", "town" -> "Camberwick Green"|>|>|>
Out[7]=
Image
In[8]:=
ResourceFunction["NestedAssociate"][contacts, {"Abigail"} ->
  <|
   "phone" ->
    <|"home" -> "9876", "mobile" -> "5432"|>,
   "address" ->
    <|"street" -> "6 Plane Drive", "town" -> "Chigley"|>|>]
Out[8]=
Image

Update an existing value deep in the nested association:

In[9]:=
contacts = <|"Joan" -> <|"phone" -> <|"home" -> "1234", "mobile" -> "5678"|>, "address" -> <|"street" -> "2 Acacia Drive", "town" -> "Trumpton"|>|>, "Mike" -> <|"phone" -> <|"home" -> "2468", "mobile" -> "1357"|>, "address" -> <|"street" -> "4 Elm Close", "town" -> "Camberwick Green"|>|>, "Abigail" -> <|"phone" -> <|"home" -> "9876", "mobile" -> "5432"|>, "address" -> <|"street" -> "6 Plane Drive", "town" -> "Chigley"|>|>|>
Out[9]=
Image
In[10]:=
ResourceFunction[
 "NestedAssociate"][contacts, {"Joan", "address", "street"} ->
  "8 Sycamore Avenue"]
Out[10]=
Image

Properties and Relations (4) 

When appending a new key/value pair, NestedAssociate is equivalent to Append, but with different key syntax:

In[11]:=
ResourceFunction[
 "NestedAssociate"][<|1 -> "one", 2 -> <|21 -> "two.1", 22 -> "two.2"|>|>, {3} -> "THREE"]
Out[11]=
Image
In[12]:=
Append[<|1 -> "one", 2 -> <|21 -> "two.1", 22 -> "two.2"|>|>, 3 -> "THREE"]
Out[12]=
Image

NestedAssociate guarantees the order of keys is maintained, whereas Append does not:

In[13]:=
ResourceFunction[
 "NestedAssociate"][<|1 -> "one", 2 -> <|21 -> "two.1", 22 -> "two.2"|>|>, {1} -> "ONE"]
Out[13]=
Image
In[14]:=
Append[<|1 -> "one", 2 -> <|21 -> "two.1", 22 -> "two.2"|>|>, 1 -> "ONE"]
Out[14]=
Image

Values can be retrieved from a nested association using resource function NestedLookup:

In[15]:=
ResourceFunction["NestedLookup"][<|1 -> "one", 2 -> <|21 -> "two.1", 22 -> "two.2"|>|>, {2, 21}]
Out[15]=
Image

Keys can be dropped from a nested association using resource function NestedKeyDrop:

In[16]:=
ResourceFunction["NestedKeyDrop"][<|1 -> "one", 2 -> <|21 -> "two.1", 22 -> "two.2"|>|>, {2, 21}]
Out[16]=
Image

Possible Issues (4) 

Where keys are themselves lists, they must be wrapped in Key:

In[17]:=
ResourceFunction[
 "NestedAssociate"][<|1 -> "one", 2 -> "two", 3 -> "three"|>, {3, Key[{31, 31}]} -> "list.31.31"]
Out[17]=
Image

Not wrapping a list-key in Key returns a Failure:

In[18]:=
ResourceFunction[
 "NestedAssociate"][<|1 -> "one", 2 -> "two", 3 -> "three"|>, {3, {31, 31}} -> "list.31.31"]
Out[18]=
Image

Where keys are themselves keys, they must be given in the form Key[Key[key]]:

In[19]:=
ResourceFunction[
 "NestedAssociate"][<|1 -> "one", 2 -> "two", 3 -> "three"|>, {1, Key[Key[11]]} -> "Key@11"]
Out[19]=
Image

NestedAssociate strips off the first Key in each key of the key sequence:

In[20]:=
ResourceFunction[
 "NestedAssociate"][<|1 -> "one", 2 -> "two", 3 -> "three"|>, {Key[2], Key[Key[22]]} -> "Key@22"]
Out[20]=
Image

Some built-in functions support this syntax:

In[21]:=
Lookup[<|"a" -> 1, {1, 2} -> 2|>, Key@{1, 2}]
Out[21]=
Image

Others do not:

In[22]:=
KeyExistsQ[<|"a" -> 1, {1, 2} -> 2|>, Key@{1, 2}]
Out[22]=
Image

Avoid this issue by using only strings as keys, and not wrapping keys in Key:

In[23]:=
keyTest = ResourceFunction[
  "NestedAssociate"][<|"a" -> "A", "b" -> "B"|>, {"b"} -> "B"]
Out[23]=
Image
In[24]:=
Lookup[keyTest, "b"]
Out[24]=
Image
In[25]:=
KeyExistsQ[keyTest, "b"]
Out[25]=
Image

Where RuleDelayed is used in the given association, NestedAssociate may cause undesired evaluation:

In[26]:=
ResourceFunction["NestedAssociate"][
 <|1 :> <|11 -> Print["Oh no!"], 12 -> Print["I've Evaluated"]|>, 2 -> "two"|>, {1, 13} -> "1.13"]
Image
Image
Image
Image
Image
Image
Out[26]=
Image

Publisher

Robert Ferguson

Version History

  • 2.0.0 – 27 December 2019
  • 1.0.0 – 23 October 2019

Related Resources

Author Notes

This is the second version of this function.
I fixed a bug that caused keys to be dropped in some instances.
Note that in this version, the keys of the supplied Association will maintain their original order. Apologies for any issues this change causes, however I feel that this is a necessary and worthwhile change that maintains naming consistency with the system function AssociateTo.
Please get in contact if there are any bugs, issues or suggestions.

License Information