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]=

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]=

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]=

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]=

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]=

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]=

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]=
In[8]:=
ResourceFunction["NestedAssociate"][contacts, {"Abigail"} ->
  <|
   "phone" ->
    <|"home" -> "9876", "mobile" -> "5432"|>,
   "address" ->
    <|"street" -> "6 Plane Drive", "town" -> "Chigley"|>|>]
Out[8]=

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]=
In[10]:=
ResourceFunction[
 "NestedAssociate"][contacts, {"Joan", "address", "street"} ->
  "8 Sycamore Avenue"]
Out[10]=

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]=
In[12]:=
Append[<|1 -> "one", 2 -> <|21 -> "two.1", 22 -> "two.2"|>|>, 3 -> "THREE"]
Out[12]=

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]=
In[14]:=
Append[<|1 -> "one", 2 -> <|21 -> "two.1", 22 -> "two.2"|>|>, 1 -> "ONE"]
Out[14]=

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]=

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]=

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]=

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]=

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]=

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]=

Some built-in functions support this syntax:

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

Others do not:

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

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]=
In[24]:=
Lookup[keyTest, "b"]
Out[24]=
In[25]:=
KeyExistsQ[keyTest, "b"]
Out[25]=

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"]
Out[26]=

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