# have a function redefine itself as it maps through a list

I have a list which contains sublists, e.g.

list = {{a,b}, {a,b,c}, {a,b,a,a}, {b,c}, ...}


I then map a function through each sublist. This function should replace each occurrence of a by (say) a number, starting from 1 and increasing up to the total number of occurrences of a, like so:

Map[f,list,{2}]
(*{{1,b}, {1,b,c}, {1,b,2,3}, {b,c}, ...}*)


I remember I once saw a very clever implementation of something similar using memoization, but I could be mistaken. How can I write a suitable function f that would do do the job?

## 3 Answers

Without memoization but works too :)

Block[{i}, (i = 1; # /. a :> i++)] & /@ {{a, b}, {a, b, c}, {a, b, a, a}, {b, c}}


And with:

ClearAll[f];
f[a, _Integer] = 0;
f[a, {p_, _}] := f[a, p] += 1;
f[x_, _] := x;

MapIndexed[f,
{{a, b}, {a, b, c}, {a, b, a, a}, {b, c}},
{2}
]

{{1, b}, {1, b, c}, {1, b, 2, 3}, {b, c}}

• Damn, 40 secs :)- Add a scoping construct for i, please:) – Dr. belisarius Sep 15 '14 at 17:47
• @Ziofil Please don't. It would be too soon :) – Kuba Sep 15 '14 at 17:51
• By the way, I used Block on the function instead of including the list: Block[{i = 1}, # /. a :> i++] & /@list, so that my function f is well defined – Ziofil Sep 15 '14 at 17:53
• +1. Localizing i is quite difficult in this context. Block is not sufficient -- try it with the list {{a, a, i, a}}. The usual cure, Module, won't work here either due to inner scope renaming. It will take some gymnastics to localize i properly: Module[{i}, Module @@@ (Hold[{i = 1}, # /. a :> i++] &)]. @belisarius – WReach Sep 16 '14 at 3:54
• Another localization option is to avoid using a pure function, e.g. Module[{f, i = 1}, f[x_] := x /. a :> i++; f] /@ {{a, a, i, a}}. – WReach Sep 16 '14 at 4:14

Here is a crude way using FoldList.

 foo[lis_, var_] := Module[{i, f}, f[var] := ++i;
f[x_List] := f /@ x;
f[x_] := x;
Rest @ FoldList[(i = 0; f[#2]) &, var, lis]
]


Use:

foo[list, a]

{{1, b}, {1, b, c}, {1, b, 2, 3}, {b, c}}


Here is the closurized way that the OP might have had in mind:

closfunc[l_]:= With[{foo = Module[{num}, num = 1; If[# == "a", num++, #] &]},
Map[foo, l]]


Now Map[closfunc, listoflists] will do the right thing.