28

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.

kglr
  • 394,356
  • 18
  • 477
  • 896
Andrew
  • 10,569
  • 5
  • 51
  • 104

12 Answers12

23

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"}}} 
*)
Sjoerd C. de Vries
  • 65,815
  • 14
  • 188
  • 323
20

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"]}}}}} *)
kglr
  • 394,356
  • 18
  • 477
  • 896
15

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] ]
Mr.Wizard
  • 271,378
  • 34
  • 587
  • 1,371
  • @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
14
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}]
Rojo
  • 42,601
  • 7
  • 96
  • 188
13

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"}}}
Mr.Wizard
  • 271,378
  • 34
  • 587
  • 1,371
Alexei Boulbitch
  • 39,397
  • 2
  • 47
  • 96
11

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"}}}
Mark Adler
  • 4,949
  • 1
  • 22
  • 37
10

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"}}}
DavidC
  • 16,724
  • 1
  • 42
  • 94
10

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

Map[{f@First@#, Last@#} &, A, {2}]
user1066
  • 17,923
  • 3
  • 31
  • 49
9

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

MapAt[f,A,{All,All,1}]
Murta
  • 26,275
  • 6
  • 76
  • 166
9

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}}}
Jagra
  • 14,343
  • 1
  • 39
  • 81
7
A /. a_?(StringMatchQ[#, DigitCharacter ..] &) :> f[a] // Quiet
unstable
  • 1,497
  • 15
  • 14
1
 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"}}}

eldo
  • 67,911
  • 5
  • 60
  • 168