26
$\begingroup$

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.

$\endgroup$

11 Answers 11

21
$\begingroup$

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"}}} 
*)
$\endgroup$
19
$\begingroup$

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"]}}}}} *)
$\endgroup$
  • 1
    $\begingroup$ This one deserves more credit, +1 $\endgroup$ – Rojo May 18 '12 at 20:31
15
$\begingroup$

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] ]
$\endgroup$
  • $\begingroup$ @David I'm not sure how to read that; is the code hard to read? (I think ReplacePart is quite readable myself.) $\endgroup$ – Mr.Wizard May 19 '12 at 13:21
  • $\begingroup$ 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. $\endgroup$ – DavidC May 19 '12 at 13:43
13
$\begingroup$
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}]
$\endgroup$
  • $\begingroup$ This solution intrigues me, but I don't think it all got posted $\endgroup$ – Jagra May 18 '12 at 20:16
  • $\begingroup$ It makes the changes in place. Why does it intrigue you @Jagra? $\endgroup$ – Rojo May 18 '12 at 20:19
  • $\begingroup$ A construct (A[[All,All,1]] =), that I just never thought of using before. Except I can't get it work properly ;-( $\endgroup$ – Jagra May 18 '12 at 20:23
  • 1
    $\begingroup$ @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 $\endgroup$ – Rojo May 18 '12 at 20:25
  • 1
    $\begingroup$ Also, don't look at the output of that line. Look at the value of A after running the line $\endgroup$ – Rojo May 18 '12 at 20:26
12
$\begingroup$

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"}}}
$\endgroup$
11
$\begingroup$

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"}}}
$\endgroup$
10
$\begingroup$

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"}}}
$\endgroup$
9
$\begingroup$

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

Map[{f@First@#, Last@#} &, A, {2}]
$\endgroup$
8
$\begingroup$

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}}}
$\endgroup$
8
$\begingroup$

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

MapAt[f,A,{All,All,1}]
$\endgroup$
  • 4
    $\begingroup$ It might be nice to mention that Kuba brought this to your attention. $\endgroup$ – Mr.Wizard Aug 27 '13 at 4:25
  • $\begingroup$ I agree. Done :) $\endgroup$ – Murta Aug 27 '13 at 11:24
  • $\begingroup$ even MapAt[f,A,{;;,;;,1}] $\endgroup$ – garej Jan 29 '16 at 9:22
7
$\begingroup$
A /. a_?(StringMatchQ[#, DigitCharacter ..] &) :> f[a] // Quiet
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.