7
$\begingroup$

I'm attempting to MapThread a function of two lists that requires the index of the list values. For example,

MapThread[#1*i+#2*j&,{{a,b,c},{e,f,g}}]

Where #1 represents the value from list 1, #2 the value from list 2, i the index of list 1, and j the index of list 2. The expected output is

{a+e,2b+2f,3c+3g}

This would presumably be accomplished by an "IndexedMapThread" function, but I'm not sure something like that exists in Mathematica currently.

Any suggestions on how to do this simply?

$\endgroup$
9
$\begingroup$

Update:

ClearAll[imtF]
imtF[foo_] := Module[{i = 1}, foo[#, i++] & /@ Transpose@#] &

Examples:

imtF[#2 (Plus @@ #) &][{{a, b, c}, {e, f, g}}]
(* {a + e, 2 (b + f), 3 (c + g)} *)

xx = {{a, b, c}, {e, f, g}, {x, y, z}};
imtF[#2 (Plus @@ #) &][xx]
(* {a + e + x, 2 (b + f + y), 3 (c + g + z)} *)

imtF[Plus @@ Times@## &][xx]
(* {a + e + x, 2 b + 2 f + 2 y, 3 c + 3 g + 3 z} *)

fn[x_, i_] := #*i + #2*i + #3^i & @@ x (*Mr.W's example modified *)
imtF[fn][xx]
(* {a + e + x, 2 b + 2 f + y^2, 3 c + 3 g + z^3} *)

Range[Length@#] Thread@+## &[{a, b, c}, {e, f, g}]
MapIndexed[# #2[[1]] &, Thread[Plus[##]]] &[{a, b, c}, {e, f, g}]
MapIndexed[# #2[[1]] &, +## & @@@ ({##}\[Transpose])] &[{a, b, c}, {e, f, g}]
MapIndexed[+(## & @@ # ) #2[[1]] &, #\[Transpose]] &@{{a, b, c}, {e, f, g}}

all give

(* {a + e, 2 (b + f), 3 (c + g)} *)
$\endgroup$
  • $\begingroup$ aaahh, penguins working on the same q/a as you again, tell me which is neater ? $\endgroup$ – penguin77 Apr 15 '15 at 18:50
  • 1
    $\begingroup$ It seems to me than none of this is general. By my reading the OP is not asking merely for a particular mathematical operation but rather a general method of application. $\endgroup$ – Mr.Wizard Apr 15 '15 at 19:23
  • $\begingroup$ @penguin77 it is a small world :) $\endgroup$ – kglr Apr 15 '15 at 19:24
  • $\begingroup$ @Mr.Wizard, good point, thank you. Updated with something that works with general two-arg functions. $\endgroup$ – kglr Apr 15 '15 at 19:57
  • $\begingroup$ Now you get my vote. :-) $\endgroup$ – Mr.Wizard Apr 15 '15 at 20:09
7
$\begingroup$

You may consider this

MapIndexed[Times, #] & /@ {{a, b, c}, {e, f, g}}  // Plus @@ # & 

(* {{a + e}, {2 b + 2 f}, {3 c + 3 g}} *)

MapIndexed applies a function (in your example Times to all elements of the list giving part specification (in your example i respectively j) as the second argument. The two resulting lists are then added in the postfix expression.

You may flatten to get

 % //Flatten 
(* {a + e, 2 b + 2 f, 3 c + 3 g} *)

Update for fun. I know it's not part of q/a. However the numerous different answers are well suited for gaining insight on performance. Here the results with a data set {Range @ 10^6, Range @ 10^6}. (kguler's examples correspond to the non updated version posted). kguler's Range[Length@#] Thread@+## &[*dataset*] is about 45 times faster than the slowest solution proposed. This is simply amazing and gives ground for analyzing what makes one approach faster than another.

enter image description here

PS: Interested in ColorFunction -> AntarcticColor ? Here the "receipt": AntarcticColor[ z_ ] := RGBColor[z/2, 1 - z, 1];

PS,PS: hmmm, Wizards waddling behind penguins ?!

$\endgroup$
  • $\begingroup$ ++1 for AntarcticColor:) $\endgroup$ – kglr Apr 15 '15 at 21:50
4
$\begingroup$

Since it would seem that your index values i and j will always be the same you need only to Transpose your input and use MapIndexed:

MapIndexed[
  #[[1]]*#2[[1]] + #[[2]]*#2[[1]] &,
  {{a, b, c}, {e, f, g}}\[Transpose]
]
{a + e, 2 b + 2 f, 3 c + 3 g}

Here #[[1]] is the first element, #[[2]] is the second element, and #2[[1]] is the (universal) index.


To make this easier to use consider rewriting your function as follows:

fn[x_, {i_}] := #*i + #2*i + #3^i & @@ x

MapIndexed[fn, {{a, b, c}, {e, f, g}, {x, y, z}}\[Transpose]]
{a + e + x, 2 b + 2 f + y^2, 3 c + 3 g + z^3}

A third parameter is included for illustration. i represents the universal index.

$\endgroup$
4
$\begingroup$

At face value there is this solution:

IndexedMapThread[list1_,list2_] := 
  MapThread[(#1*#3+#2*#4 &),{list1,list2,Range@Length@list1,Range@Length@list2}]

IndexedMapThread[{a, b, c}, {d, e, f}]
  (* {a + d, 2 b + 2 e, 3 c + 3 f} *)
$\endgroup$
  • $\begingroup$ This is throwing an error at me "MapThread called with 5 arguments $\endgroup$ – penguin77 Apr 15 '15 at 19:38
  • 1
    $\begingroup$ Crap, sorry, I forgot to wrap the lists and ranges into curly braces. Fixed. And it still got upvoted :D, I guess everybody just looked at the code, thought "seems legit" and didn't test it :-) $\endgroup$ – LLlAMnYP Apr 15 '15 at 19:40
  • $\begingroup$ Incidentally one could assume that the lists are the same length and use: MapThread[(#1*#3+#2*#4 &),{list1,list2, #, #}] & @ Range @ Length @ list1 or the equivalent With. $\endgroup$ – Mr.Wizard Apr 15 '15 at 19:43
  • $\begingroup$ I'm a bit concerned about possible conflicts of #1 and # in this code snippet (what if it substitutes the range into the first argument of MapThread? I don't have Mathematica handy right now to check), but following the gist of your comment I'd prefer then MapThread[(#3(#1+#2)&),{list1,list2,#}] & @ Range @ Length @ list1. No need to assume anything, as MapThread will throw an error if the lists are not of the same length anyway. $\endgroup$ – LLlAMnYP Apr 15 '15 at 19:47
  • $\begingroup$ @LLlAMnYP, it also already happened to me. HINT: Always copy immediately the posted code back into Notebook and evaluate, for cross-checking. Making it a good habit. $\endgroup$ – penguin77 Apr 15 '15 at 19:49
2
$\begingroup$

Here is definition for indexedMapThread that works for any number of lists so long as they are all equal in length.

indexedMapThread[args : {_List ..}] :=
 Module[{sizes = Length /@ args, scalars},
  If[Not[Equal @@ sizes], Return[$Failed]];
  scalars = Range@sizes[[1]];
  Expand @ Flatten @ Thread[{#1 Plus[##2]}&[scalars, Sequence @@ args]]]

indexedMapThread @ {{a, b, c}}
{a, 2 b, 3 c}
indexedMapThread @ {{a, b, c}, {e, f, g}}
{a + e, 2 b + 2 f, 3 c + 3 g}
indexedMapThread @ {{a, b, c}, {e, f, g}, {h, i, j}}
{a + e + h, 2 b + 2 f + 2 i, 3 c + 3 g + 3 j}
$\endgroup$
1
$\begingroup$

Not sure why you'd need differing variable names, since the index would always be same, but, that aside:

imt[f_, l_, v_] := Module[Evaluate@v, MapThread[(v = ConstantArray[#3, Length@v]; f) &, 
                    Append[l, Range@Length@l[[1]]]]];

Your example (note last entry is names of variables to be realized):

imt[#1*i + #2*j, {{a, b, c}, {e, f, g}}, {i, j}]
(* {a + e, 2 b + 2 f, 3 c + 3 g} *)
$\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.