# Map-Thread-Through-Apply a list of functions onto a list of (lists of) values

I have a list of functions:

fns = {f, g, h}


and a list of triples:

list = {{1,2,3},{11,22,33},{111,222,333},{1111,2222,3333}};


What's the best way to apply f to the first element of every triple, g to the second elements, and h to the last elements?

{
{f[1], g[2], h[3]},
{f[11], g[22], h[33]},
{f[111], g[222], h[333]},
{f[1111], g[2222], h[3333]}
}


(I know a few methods, but I'm looking for more.)

• @J.M. I know, which is why I'm amused that three answers below are using it. – Mr.Wizard Sep 30 '12 at 9:50
• @Mr.Wizard You sure it were you? Have a look here, here, and here,for example :-). – Leonid Shifrin Sep 30 '12 at 13:26
• @Leonid three replies come to mind: (1) You don't think I actually read all that stuff do you? (2) I'm senile and I have no idea why they trust me with the keys. (3) Great minds think alike. -- Take your pick. – Mr.Wizard Sep 30 '12 at 13:42
• @Mr.Wizard I'dpick #3 :-). Besides, neither of us started the trend, it was started by the designer of mma, responsible for Compose (in this case, most likely Stephen Wolfram himself). – Leonid Shifrin Sep 30 '12 at 13:46
• @WReach, if my opinion counts for anything, I was bummed when they "replaced" Compose[] with Composition[]. Oh well... – J. M. will be back soon Sep 30 '12 at 14:06

Inner[#2@#1 &, list, fns, List, 2]


or

Inner[Compose, fns, Transpose@list, List] (* Note, that Compose is obsolete *)


or

MapIndexed[fns[[Last@#2]]@#1 &, list, {2}]


or

ListCorrelate[{fns}, list, {1, -1}, {}, Compose, Sequence]


or

MapThread[Compose, {Array[fns &, Length@list], list}, 2]


or

ReplacePart[list, {i_, j_} :> fns[[j]][list[[i, j]]]]


or

list // Query[All, Thread[Range@Length@fns -> fns]]


or (cheating a little)

list // Query[All, {1 -> f, 2 -> g, 3 -> h}]

• The Inner approach is the one I'm currently using. – Brett Champion Sep 30 '12 at 1:28
• I added another method to make up for it :) – WReach Sep 30 '12 at 1:48
• Like J.M. says Compose has been obsolete since more than 20(!) years. – stevenvh Oct 3 '12 at 17:12
• @steven, that's nothing; Mathematica 8 still has Release[], and that has been deprecated far longer... – J. M. will be back soon Oct 3 '12 at 17:16
• @J.M. "far longer" seems a bit difficult; according to the documentation center both are obsolete since version 2 (1991), and I can't imagine functions being deprecated starting version 1 :-). – stevenvh Oct 3 '12 at 17:19

Map[MapThread[Compose, {fns, #}] &, list]


or

Transpose@MapThread[Map, {fns, Transpose[list]}]

• Like J.M. says Compose has been obsolete since more than 20(!) years. – stevenvh Oct 3 '12 at 17:13
• @stevenvh And so :-) ? – Leonid Shifrin Oct 3 '12 at 17:15
• @steven, it may not exactly be recommended, but it still works. From an old army saw: "if a dumb thing works, then it ain't dumb." – J. M. will be back soon Oct 3 '12 at 17:23
• @Leonid - The documentation center doesn't give any description of it any more, so how can it help OP, when he doesn't know what it does? (You don't explain it either) – stevenvh Oct 4 '12 at 15:53
• @stevenvh Fair enough. I will add the description. But I guess the OP would know what it is, in this particular case, given his affiliation :-) – Leonid Shifrin Oct 4 '12 at 15:58

The OP said: "I know a few methods, but I'm looking for more." so here are my offerings for the sake of interest. The second is intentionally a bit convoluted. The third may actually be of interest as the method could be used for in-place modification.

With[{op = MapIndexed[#[Slot @@ #2] &, fns]}, op & @@@ list]

Fold[RotateLeft@MapAt[#2, #, 1] &, list\[Transpose], Function[x, x /@ # &] /@ fns]\[Transpose]

Module[{x = list\[Transpose]}, Table[x[[i]] = fns[[i]] /@ x[[i]], {i, Length@x}]; x\[Transpose]]


Or for in-place modification:

With[{x = list}, Table[x[[All, i]] = fns[[i]] /@ x[[All, i]], {i, Length@First@x}]; x]


This post is primarily to provide the service of comparative timings. I will be using Mathematica 7.

Timings using an array of 1.5 million Integers and three inert symbolic heads:

fns = {f, g, h};
list = RandomInteger[1*^6, {500000, 3}];
times = timeAvg[#[]] & /@ methods;


Using an array of Reals and three trig functions:

fns = {Sin, Cos, Csc};
list = RandomReal[1*^6, {500000, 3}];
times = timeAvg[#[]] & /@ methods;


To explore performance with different shapes here is as above but with 500 random trig functions:

fns = RandomChoice[{Sin, Cos, Sec, Csc, Tan}, 500];
list = RandomReal[1*^6, {5000, 500}];
times = timeAvg[#[]] & /@ methods;


Functions as I named and used them:

SetAttributes[timeAvg, HoldFirst]
timeAvg[func_] :=
Do[If[# > 0.3, Return[#/5^i]] & @@ Timing@Do[func, {5^i}], {i, 0, 15}]

leonid1[] := Map[MapThread[Compose, {fns, #}] &, list]
rm1[]     := Replace[list, x_List :> MapIndexed[fns[[First@#2]]@#1 &, x], {1}]
rm2[]     := MapIndexed[fns[[First@#2]]@#1 &, #] & /@ list
kguler1[] := Inner[#1@#2 &, fns, #, List] & /@ list
kguler2[] := Inner[Compose, fns, #, List] & /@ list
wreach1[] := Inner[#2@#1 &, list, fns, List, 2]
wreach2[] := MapIndexed[fns[[Last@#2]]@#1 &, list, {2}]
wreach3[] := ListCorrelate[{fns}, list, {1, -1}, {}, Compose, Sequence]
wreach4[] := MapThread[Compose, {Array[fns &, Length@list], list}, 2]
wizard1[] := With[{op = MapIndexed[#[Slot @@ #2] &, fns]}, op & @@@ list]
wizard2[] := Fold[RotateLeft@MapAt[#2, #, 1] &, list\[Transpose], Function[x, x /@ # &] /@ fns]\[Transpose]
wizard3[] := Module[{x = list\[Transpose]}, Table[x[[i]] = fns[[i]] /@ x[[i]], {i, Length@x}]; x\[Transpose]]

methods = {leonid1, leonid2, rm1, rm2, kguler1, kguler2, wreach1,
wreach2, wreach3, wreach4, wizard1, wizard2, wizard3};

Inner[#1@#2 &, fns, #, List] & /@ list
(*or *)
Inner[Compose, fns, #, List] & /@ list
% //TableForm


• Like J.M. says Compose has been obsolete since more than 20(!) years. – stevenvh Oct 3 '12 at 17:14
• @stevenvh And it still works just fine, thank you. :D – Mr.Wizard Jul 15 '14 at 8:42

Another solution using MapIndexed and —

1. Replace:

Replace[list, x : {_, _, _} :> MapIndexed[fns[[First@#2]]@#1 &, x], {1}]

2. Map:

MapIndexed[fns[[First@#2]]@#1 &, #] & /@ list


Here is another option using Compose:

Compose@@@Thread@{fns, #}&/@list


or with Function:

Thread[fns~Function[{f, v}, f@v, Listable]~#] & /@ list