# 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
Commented May 18, 2012 at 20:31

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

• @David I'm not sure how to read that; is the code hard to read? (I think ReplacePart is quite readable myself.) Commented May 19, 2012 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. Commented May 19, 2012 at 13:43
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 Commented May 18, 2012 at 20:16
• It makes the changes in place. Why does it intrigue you @Jagra?
– Rojo
Commented May 18, 2012 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 ;-( Commented May 18, 2012 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
Commented May 18, 2012 at 20:25
• Also, don't look at the output of that line. Look at the value of A after running the line
– Rojo
Commented May 18, 2012 at 20:26

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"}}}


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"}}}


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

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


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}}}


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. Commented Aug 27, 2013 at 4:25
• I agree. Done :) Commented Aug 27, 2013 at 11:24
• even MapAt[f,A,{;;,;;,1}] Commented Jan 29, 2016 at 9:22
A /. a_?(StringMatchQ[#, DigitCharacter ..] &) :> f[a] // Quiet

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


1.

Since V 13.1 there is ReplaceAt:

ReplaceAt[list, a_ :> f[a], {All, All, 1}]


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

2.

We can use it to apply f only if the first list member is a number string:

list = {
{{"xx", "CG"}, {"391", "CG"}, {"yy", "CC3"}},
{{"3", "CG"}, {"16", "CG"}, {"392", "CG"}}};

ReplaceAt[list,
a_?(StringMatchQ[#, NumberString] &) :> Log @ N @ ToExpression[a], {All, All, 1}]


{{{"xx", "CG"}, {5.96871, "CG"}, {"yy", "CC3"}}, {{1.09861, "CG"}, {2.77259, "CG"}, {5.97126, "CG"}}}

3.

I also like Query:

Query[All, All, {1 -> f}] @ list


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