# Using MapAt to map at a particular depth (levelspec)

I have the following sample list (my actual list is, of course, much longer):

A={{{"15", "CG"}, {"391", "CG"}, {"412", "CC3"}}, {{"3", "CG"},
{"16", "CG"}, {"392", "CG"}}};


I would like to map an arbitrary function f onto the string representation of numbers, like this:

{{{f["15"], "CG"}, {f["391"], "CG"}, {f["412"], "CC3"}}, {{f["3"], "CG"},
{f["16"], "CG"}, {f["392"], "CG"}}}


Is there a straightforward, succinct way of doing this using Map, MapAt, or something else? Unlike Map, it seems that MapAt does not have an option with levelspec.

-

A combination of Map and MapAt perhaps?

Map[MapAt[f, #, 1] &, A, {2}]

(* ===>  {{{f["15"], "CG"}, {f["391"], "CG"}, {f["412"], "CC3"}},
{{f["3"], "CG"}, {f["16"], "CG"}, {f["392"], "CG"}}}
*)

-

To map f to number strings appearing at any position or depth:

Map[If[StringMatchQ[#, NumberString], f[#], #] &, A, {-1}]
(* => {{{f["15"], "CG"}, {f["391"], "CG"}, {f["412"], "CC3"}}, {{f["3"], "CG"}, {f["16"], "CG"}, {f["392"], "CG"}}} *)


another example:

b = {{{"15", "CG"}, {{"391", "CG"}, "230"}, {"412", "CC3"}}, {{"3",
"CG"}, {"392", {"CG", {"CG", "345"}}}}};
Map[If[StringMatchQ[#, NumberString], f[#], #] &, b, {-1}]
(* => {{{f["15"], "CG"}, {{f["391"], "CG"}, f["230"]}, {f["412"], "CC3"}}, {{f["3"], "CG"}, {f["392"], {"CG", {"CG", f["345"]}}}}} *)

-
This one deserves more credit, +1 – Rojo May 18 '12 at 20:31
Thank you @Rojo. – kglr May 18 '12 at 21:43

Perhaps

 g = # /. {a_String, b_String} :>  {f[a], b} &;
g@A


yielding

{{{f["15"], "CG"}, {f["391"], "CG"}, {f["412"], "CC3"}}, {{f["3"],
"CG"}, {f["16"], "CG"}, {f["392"], "CG"}}}

-

Map on map does the job nicely:

{f[#[[1]]], #[[2]]} & /@ # & /@ A

{{{f[15],CG},{f[391],CG},{f[412],CC3}},{{f[3],CG},{f[16],CG},{f[392],CG}}}

-
A[[All, All, 1]] = Map[f, A[[All, All, 1]], {2}]


I think the neatest given the question is probably a mix between David Carraher and kguler's

A /. x_String /; StringMatchQ[x, NumberString] :> f[x]


or a little bit more efficiently, searching only the lowest levels

Replace[A, x_String /; StringMatchQ[x, NumberString] :> f[x], {-1}]

-
This solution intrigues me, but I don't think it all got posted – Jagra May 18 '12 at 20:16
It makes the changes in place. Why does it intrigue you @Jagra? – Rojo May 18 '12 at 20:19
A construct (A[[All,All,1]] =), that I just never thought of using before. Except I can't get it work properly ;-( – Jagra May 18 '12 at 20:23
@Jagra, since the changes are made in place, you can use it only once. If you run it twice, you get f[f["23"]], etc – Rojo May 18 '12 at 20:25
Also, don't look at the output of that line. Look at the value of A after running the line – Rojo May 18 '12 at 20:26

Another possibility, which I don't think has been given (but is similar to other answers):

Map[{f@First@#, Last@#} &, A, {2}]

-
A /. a_?(StringMatchQ[#, DigitCharacter ..] &) :> f[a] // Quiet

-

I like to use @@ (or @@@ on lists) since #1 is easier to read than #[[1]] in the middle (muddle) of more brackets:

Map[{f[#1], #2} & @@ # &, A, {2}]

{{{f["15"], "CG"}, {f["391"], "CG"}, {f["412"], "CC3"}}, {{f["3"],
"CG"}, {f["16"], "CG"}, {f["392"], "CG"}}}

-

More options:

Apply[{f[#1], ##2} &, A, {2}]

{f[#1], ##2} & @@@ # & /@ A

ReplacePart[A, {i_, j_, 1} :> f @ A[[i, j, 1]] ]

ReplacePart[A, p:{_, _, 1} :> f @ Extract[A, p] ]

-
+1 For the graffiti. – DavidC May 19 '12 at 13:15
@David I'm not sure how to read that; is the code hard to read? (I think ReplacePart is quite readable myself.) – Mr.Wizard May 19 '12 at 13:21
The second example would be difficult to read for the newly initiated in mma. My +1 more accurately reflects my amazement at your skill in crafting so many different solutions. – DavidC May 19 '12 at 13:43

Try a very simple idea:

A = {{{"15", "CG"}, {"391", "CG"}, {"412", "CC3"}}, {{"3",
"CG"}, {"16", "CG"}, {"392", "CG"}}};

g[{x_, y_}] := {f[x], y};

Map[g, A, {2}]

{{{f["15"], "CG"}, {f["391"], "CG"}, {f["412"], "CC3"}}, {{f["3"],
"CG"}, {f["16"], "CG"}, {f["392"], "CG"}}}

-

As @Kuba shows here, now in version 9 you can do:

MapAt[f,A,{All,All,1}]

-
It might be nice to mention that Kuba brought this to your attention. – Mr.Wizard Aug 27 '13 at 4:25
I agree. Done :) – Murta Aug 27 '13 at 11:24
even MapAt[f,A,{;;,;;,1}] – garej Jan 29 at 9:22