36
$\begingroup$

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.)

$\endgroup$
12
  • 1
    $\begingroup$ @J.M. I know, which is why I'm amused that three answers below are using it. $\endgroup$
    – Mr.Wizard
    Commented Sep 30, 2012 at 9:50
  • 1
    $\begingroup$ @Mr.Wizard You sure it were you? Have a look here, here, and here,for example :-). $\endgroup$ Commented Sep 30, 2012 at 13:26
  • 1
    $\begingroup$ @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. $\endgroup$
    – Mr.Wizard
    Commented Sep 30, 2012 at 13:42
  • 1
    $\begingroup$ @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). $\endgroup$ Commented Sep 30, 2012 at 13:46
  • 1
    $\begingroup$ @WReach, if my opinion counts for anything, I was bummed when they "replaced" Compose[] with Composition[]. Oh well... $\endgroup$ Commented Sep 30, 2012 at 14:06

12 Answers 12

32
$\begingroup$

How about:

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}]
$\endgroup$
6
  • $\begingroup$ The Inner approach is the one I'm currently using. $\endgroup$ Commented Sep 30, 2012 at 1:28
  • $\begingroup$ I added another method to make up for it :) $\endgroup$
    – WReach
    Commented Sep 30, 2012 at 1:48
  • $\begingroup$ Like J.M. says Compose has been obsolete since more than 20(!) years. $\endgroup$
    – stevenvh
    Commented Oct 3, 2012 at 17:12
  • $\begingroup$ @steven, that's nothing; Mathematica 8 still has Release[], and that has been deprecated far longer... $\endgroup$ Commented Oct 3, 2012 at 17:16
  • $\begingroup$ @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 :-). $\endgroup$
    – stevenvh
    Commented Oct 3, 2012 at 17:19
27
$\begingroup$

What about

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

or

Transpose@MapThread[Map, {fns, Transpose[list]}]
$\endgroup$
7
  • 1
    $\begingroup$ Like J.M. says Compose has been obsolete since more than 20(!) years. $\endgroup$
    – stevenvh
    Commented Oct 3, 2012 at 17:13
  • 1
    $\begingroup$ @stevenvh And so :-) ? $\endgroup$ Commented Oct 3, 2012 at 17:15
  • 2
    $\begingroup$ @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." $\endgroup$ Commented Oct 3, 2012 at 17:23
  • $\begingroup$ @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) $\endgroup$
    – stevenvh
    Commented Oct 4, 2012 at 15:53
  • $\begingroup$ @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 :-) $\endgroup$ Commented Oct 4, 2012 at 15:58
23
$\begingroup$

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;
BarChart[MapThread[Labeled, {times, methods}]]

Mathematica graphics

Using an array of Reals and three trig functions:

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

Mathematica graphics

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;
BarChart[MapThread[Labeled, {times, methods}]]

Mathematica graphics


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]
leonid2[] := Transpose@MapThread[Map, {fns, Transpose[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};
$\endgroup$
16
$\begingroup$
Inner[#1@#2 &, fns, #, List] & /@ list
(*or *)
Inner[Compose, fns, #, List] & /@ list
% //TableForm

enter image description here

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

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

Here is another option using Compose:

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

or with Function:

Thread[fns~Function[{f, v}, f@v, Listable]~#] & /@ list
$\endgroup$
8
$\begingroup$

Sorry for bothering an old question. It's not that fast but here is one way with the newer Threaded:

Function[, #1@#2, {Listable}][Threaded@fns, list]
$\endgroup$
2
  • 2
    $\begingroup$ (+1) I like updates like this one!!! $\endgroup$
    – bmf
    Commented Jan 11, 2023 at 12:00
  • 3
    $\begingroup$ @bmf Thanks! Though not the fastest, the performance of Threaded is not bad. I guess people on this site haven't got into habit of using it (yet). $\endgroup$
    – Silvia
    Commented Jan 11, 2023 at 12:13
7
$\begingroup$

This is not a complete answer, but I am posting it as one so it does not get lost in the comments. Compose has returned in Version 12 as Construct, so all solutions can now be implemented with supported and documented functions.

$\endgroup$
2
  • 3
    $\begingroup$ Maybe should note that Compose[f, x] is equivalent to Construct[f, x] but Compose[f, g, x] is not equivalent to Construct[f, g, x]. $\endgroup$
    – Michael E2
    Commented Jan 8, 2020 at 21:00
  • $\begingroup$ @MichaelE2, thank you for noting this. I suspect from watching the design reviews online that there may be a builtin function that will solve this whole problem in 12.1 as it seems to have caught Wolfram's attention. $\endgroup$
    – Daniel W
    Commented Jan 8, 2020 at 21:25
5
$\begingroup$

Just another way:

Transpose@Diagonal[(Thread /@ Through[fns[#]]) & /@ (Transpose@list)]

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

-Update- Or using Outer:

Map[Diagonal]@Outer[Through@*fns @@ #1 &@*List, list]

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

$\endgroup$
3
  • 1
    $\begingroup$ Nicely done mate - comment/reminder to upvote - I used max votes today $\endgroup$
    – bmf
    Commented Jan 11, 2023 at 15:59
  • 1
    $\begingroup$ Hi, mate! We are starting the year with a lot of energy :-) $\endgroup$ Commented Jan 11, 2023 at 16:10
  • $\begingroup$ @bmf Hi, mate! See the update, please! :-) $\endgroup$ Commented Jul 15 at 23:19
4
$\begingroup$

Yet another solution:

Transpose[Activate[Thread[Inactive[Map][fns, Transpose[list]]]]]
$\endgroup$
4
$\begingroup$
fns = {f, g, h};

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

Using AssociationThread and KeyValueMap:

Transpose[KeyValueMap[Thread[#1@#2] &]@AssociationThread[fns, Thread@list]]

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

Or using Thread and MapApply as follows:

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

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

$\endgroup$
3
$\begingroup$
fns = {f, g, h};

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

Using ColumnMap by Michael Sollami

CMap = ResourceFunction["ColumnMap"];

CMap[fns, list, Range @ Length @ fns] 

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

$\endgroup$
1
  • $\begingroup$ Looking at the implementation, "ColumnMap" uses Map/Fold/MapAt, similar to wizard2. $\endgroup$ Commented Jul 22 at 15:48

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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