# Apply function to all first arguments of a nested list of rules

Currently, I have a nested list of rules of the form

lst1={1->a,2->{3->c,4->d,5->{6->f,7->{8->h,...}}}}


(where I don't know the length of the list). I would like to apply some function z to the "first" elements of the rules to get something of the form

lst2={z[1]->a,z[2]->{z[3]->c,z[4]->d,z[5]->{z[6]->f,z[7]->{z[8]->h,...}}}}.


• I don't know the length of the (nested) list.
• A priori, I also don't know the lengths of any of the inner lists. That means both {1->a,2->{3->c,4->d,5->{6->f,7->{8->h,...}}}} and {1->a,2->b,3->{4->d,5->{6->f,7->{8->h,...}}}} are equally possible / valid.

Basically, I want everything of the form Rule[_,_] to be mapped to Rule[z[_],_], but nothing I attempted (including Apply, MapAt, etc.) seems to work. I can't figure out the pattern matching to save my life.

One thing I thought would work is doing

lst1 /. {Rule[x_,y_]:>Rule[z[x],y]},


but this fails for inputs of the form lst3={1 -> {2 -> {3 -> {4 -> 5}}}}. For lst3 as above, I would want to get out

lst4={z[1]->{z[2]->{z[3]->{z[4]->5}}}},


but instead I get lst4b={z[1] -> {2 -> {3 -> {4 -> 5}}}}.

list = {1 -> a, 2 -> {3 -> c, 4 -> d, 5 -> {6 -> f, 7 -> {8 -> h, 9 -> j}}}};


1. MapAt

MapAt[z, list, Append[1] /@ Position[list, _Rule]]


{z[1] -> a, z[2] -> {z[3] -> c, z[4] -> d, z[5] -> {z[6] -> f, z[7] -> {z[8] -> h, z[9] -> j}}}}

2. ReplaceAt

ReplaceAt[list, (a_ -> b_) :> z[a] -> b, Position[list, _Rule]]


{z[1] -> a, z[2] -> {z[3] -> c, z[4] -> d, z[5] -> {z[6] -> f, z[7] -> {z[8] -> h, z[9] -> j}}}}

3. Replace (a shorter variant of Syed's solution

Replace[list, (a_ -> b_) :> z[a] -> b, -1]


{z[1] -> a, z[2] -> {z[3] -> c, z[4] -> d, z[5] -> {z[6] -> f, z[7] -> {z[8] -> h, z[9] -> j}}}}

Using Replace / Map :

lst = {1 -> a, 2 -> {3 -> c, 4 -> d, 5 -> {6 -> f, 7 -> {8 -> h}}}};

Map[Replace[
HoldPattern[a_ -> b_] -> z[a] -> b], lst, {1, ∞}]


EDIT

Thanks to @lericr:

Replace[lst, HoldPattern[a_ -> b_] :> z[a] -> b, Infinity]


would also work.

Result:

{z[1] -> a, z[2] -> {z[3] -> c, z[4] -> d, z[5] -> {z[6] -> f, z[7] -> {z[8] -> h}}}}

• You don't need the Map, do you? Wouldn't Replace[lst, HoldPattern[a_ -> b_] :> z[a] -> b, Infinity] work? Commented Nov 2, 2023 at 5:06

the rules to get something of the form

lst2={z[1]->a,z[2]->{z[3]->c,z[4]->d,z[5]->{z[6]->f,z[7]->{z[8]->h,...}}}}.

ClearAll["Global*"]
lst1={1->a,2->{3->c,4->d,5->{6->f,7->{8->h}}}}
lst1//.Rule[x_,y_]:>{z[x],y}
%//.{z[x_],any___}:>Rule[z[x],any]


You could first find all the positions of Rule, and change the last index to 1:

lst1 = {1 -> a, 2 -> {3 -> c, 4 -> d, 5 -> {6 -> f, 7 -> {8 -> h, 9 -> j}}}}
pos = ReplacePart[Position[lst1, Rule], {_, -1} -> 1]


Then use those positions in MapAt:

MapAt[f, lst1, pos]


{f[1] -> a, f[2] -> {f[3] -> c, f[4] -> d, f[5] -> {f[6] -> f, f[7] -> {f[8] -> h, f[9] -> j}}}}

lst = {1 -> a, 2 -> {3 -> c, 4 -> d, 5 -> {6 -> f, 7 -> {8 -> h}}}}

lst //.  Rule[Except[_z, a_], b_] :> Rule[z @ a, b]


{z[1] -> a, z[2] -> {z[3] -> c, z[4] -> d, z[5] -> {z[6] -> f, z[7] -> {z[8] -> h}}}}

Additional ways to use ReplaceAll and Replace:

lst = {1 -> a, 2 -> {3 -> c, 4 -> d, 5 -> {6 -> f, 7 -> {8 -> h}}}};

lst /. Rule -> MapAt[z, 1]@*Rule


{z[1] -> a, z[2] -> {z[3] -> c, z[4] -> d, z[5] -> {z[6] -> f, z[7] -> {z[8] -> h}}}}

Replace[lst, Rule -> MapAt[z, 1]@*Rule, All, Heads -> True]


{z[1] -> a, z[2] -> {z[3] -> c, z[4] -> d, z[5] -> {z[6] -> f, z[7] -> {z[8] -> h}}}}

An additional way to use ReplaceAll and Cases:

list = {1 -> a, 2 -> {3 -> c, 4 -> d, 5 -> {6 -> f, 7 -> {8 -> h, 9 -> j}}}};

# /. Cases[#, Rule[x_, y_] :> x -> z[x], ∞] &@list


{z[1] -> a, z[2] -> {z[3] -> c, z[4] -> d, z[5] -> {z[6] -> f, z[7] -> {z[8] -> h, z[9] -> j}}}}

list = {1 -> a, 2 -> {3 -> c, 4 -> d, 5 -> {6 -> f, 7 -> {8 -> h, 9 -> j}}}};


Using ReplacePart

With[{
p = Append[1] /@ Position[list, _Rule],
k = Cases[list, (x_ -> _) -> z[x], All]},
`