Take the 2-minute tour ×
Mathematica Stack Exchange is a question and answer site for users of Mathematica. It's 100% free, no registration required.

Say I have a group of functions:

f1[a_] := a * -1;
f2[a_] := a * 100;
f3[a_] := a / 10.0;

and some data in a list:

data := Range[1, 20];

I would like to apply this group of functions to the data: the first function applied to the first item of data, the second to the second, and so on. Because there are more data elements than there are functions, the first function is also applied to the fourth data element, and so on.

A simple work-round is this:

Flatten[{f1[#[[1]]], f2[#[[2]]], f3[#[[3]]]}  & /@ Partition[data, 3]]

giving

{-1, 200, 0.3, -4, 500, 0.6, -7, 800, 0.9, -10, 1100, 1.2, -13, 1400, 
  1.5, -16, 1700, 1.8}

but this isn't an ideal solution: the slots have been 'hard-wired', and it wouldn't be possible to modify the list of functions easily.

Is there a Map-related function that could do this elegantly? I've not been able to discover it yet.

(This is a toy example, of course!)

share|improve this question
3  
your current solution only goes up to 18, is that intentional? –  acl Apr 3 '12 at 10:22
5  
Congrats on the 1024th question of this site! –  F'x Apr 3 '12 at 12:02
7  
Oh good, a nice round number: xkcd.com/1000 –  tkott Apr 3 '12 at 14:04
add comment

7 Answers

up vote 19 down vote accepted

Here's how you can do it in a simple way:

functionMap[funcs_List, data_] := Module[{fn = RotateRight[funcs]}, 
    First[(fn = RotateLeft[fn])][#] & /@ data]

Use it as:

functionMap[{f1, f2, f3}, Range[20]]
(* {f1[1], f2[2], f3[3], f1[4], f2[5], f3[6], f1[7], f2[8], f3[9], f1[10],
    f2[11], f3[12], f1[13], f2[14], f3[15], f1[16], f2[17], f3[18], f1[19], f2[20]} *)
share|improve this answer
1  
Oooo, clever. +1 (you know I like that approach.) –  Mr.Wizard Apr 9 '12 at 15:22
1  
You could write it as a closure: makeRotator[funcs_] := Module[{fn = funcs}, First[(fn = RotateLeft[fn])][#] &] then makeRotator[{f1,f2,f3}] /@ Range[10]. –  Szabolcs May 25 '12 at 6:50
    
Shorter/simpler: {fn = funcs} and Last[(fn = RotateLeft[fn])] –  Mr.Wizard Jan 4 '13 at 18:01
add comment

Would the following qualify as elegant or not?

mapFunctions[funcs_, list_] := Module[{r = list, u},
  applyFunc[f_, l_, i_, t_] := 
   MapAt[f, l, Table[{u}, {u, i, Length[l], t}]];
  Do[r = applyFunc[funcs[[u]], r, u, Length[funcs]],
   {u, Length[funcs]}];
  Return[r];
  ]

which gives:

In[9]:= MapFunctions[{f1, f2, f3}, data]

Out[9]= {f1[1], f2[2], f3[3], f1[4], f2[5], f3[6], f1[7], f2[8], 
 f3[9], f1[10], f2[11], f3[12], f1[13], f2[14], f3[15], f1[16], 
 f2[17], f3[18], f1[19], f2[20]}
share|improve this answer
2  
Well, it has a Do. What would you say? ;-) –  Sjoerd C. de Vries Apr 3 '12 at 10:46
1  
@SjoerdC.deVries but it has no Goto, I swear! –  F'x Apr 3 '12 at 11:14
add comment

Use PadRight with the cyclical padding setup:

funcs = {f1, f2, f3};
data = Range[1, 20];
MapThread[#1[#2] &, {PadRight[funcs, Length@data, funcs], data}]

{f1[1], f2[2], f3[3], f1[4], f2[5], f3[6], f1[7], f2[8], f3[9], f1[10], f2[11], f3[12], f1[13], f2[14], f3[15], f1[16], f2[17], f3[18], f1[19], f2[20]}

share|improve this answer
1  
You'll get a populist badge pretty soon :) Congrats in advance! –  rm -rf May 24 '12 at 21:56
    
I wrote a solution myself before looking at the answers. It was exactly this one, nearly character by character. :) –  Szabolcs May 25 '12 at 6:44
    
@Szabolcs: Great minds when meet... :) Thanks R.M, you should know that usually I use your method. –  István Zachar May 25 '12 at 8:02
add comment
MapIndexed[{f1, f2, f3}[[Mod[First@#2, 3, 1]]][#1] &, data]

does what you want (thanks to Sjoerd for pointing out a silly inefficiency).

share|improve this answer
    
This is more or less what I was thinking about too, but I wouldn't give the arguments to the functions first. In this way you're doing 3 times more calculations than necessary. Just put a [#1] after the Part and leave the function list alone. –  Sjoerd C. de Vries Apr 3 '12 at 10:44
    
@Sjoerd yes good point, also, this way it generalizes better. thanks –  acl Apr 3 '12 at 10:58
add comment

Another approach

#1@#2 & @@@ 
  Partition[Riffle[Range[20], {f1, f2, f3}, {1, -1, 2}], 2] 

Comparing with Acl's solution:

#1@#2 & @@@ 
  Partition[Riffle[Range[20], {f1, f2, f3}, {1, -1, 2}], 2] == 
 MapIndexed[{f1@#1, f2@#1, f3@#1}[[Mod[First@#2, 3, 1]]] &, data

==> True

share|improve this answer
1  
nice one +1. Can save few key strokes with #2@#1 & @@@ Partition[Riffle[Range[20], {f1, f2, f3}], 2]:) –  kguler Apr 3 '12 at 14:02
add comment

Another approach using Fold in combination with Sow/Reap:

Reap[Fold[(Sow[#[[1]][#2]]; RotateLeft[#]) &, {f1, f2, f3}, data];][[2, 1]]
share|improve this answer
add comment

Another approach is to use Outer, as follows

Flatten@Outer[#1[#2]&, {f1, f2, f3}, Range[1,5]]
(*
{f1[1], f1[2], f1[3], f1[4], f1[5], 
 f2[1], f2[2], f2[3], f2[4], f2[5],
 f3[1], f3[2], f3[3], f3[4], f3[5]}
*)

Unfortunately, Outer causes problems if the data points are vectors, for instance

Flatten@Outer[#1[#2] &, {f1, f2, f3}, {#, #} & /@ Range[1, 3]]

produces

{f1[1], f1[1], f1[2], f1[2], f1[3], f1[3], 
 f2[1], f2[1], f2[2], f2[2], f2[3], f2[3], 
 f3[1], f3[1], f3[2], f3[2], f3[3], f3[3]}

To work around this, you need to use the 3$^\text{rd}$ and subsequent parameters of Outer which are level specifications:

Outer[#1[#2] &, {f1, f2, f3}, {#, #} & /@ Range[1, 3], Infinity, 1] //
  Flatten
(*
{f1[{1, 1}], f1[{2, 2}], f1[{3, 3}], 
 f2[{1, 1}], f2[{2, 2}], f2[{3, 3}], 
 f3[{1, 1}], f3[{2, 2}], f3[{3, 3}]}
*)

Or, if you prefer

Outer[#1 @@ #2 &, {f1, f2, f3}, {#, #} & /@ Range[1, 3], Infinity, 1] //
  Flatten
(*
{f1[1, 1], f1[2, 2], f1[3, 3], 
 f2[1, 1], f2[2, 2], f2[3, 3], 
 f3[1, 1], f3[2, 2], f3[3, 3]}
*)
share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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