9

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?

Shaggy1135
  • 171
  • 6

9 Answers9

10

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)} *)
Mr.Wizard
  • 271,378
  • 34
  • 587
  • 1,371
kglr
  • 394,356
  • 18
  • 477
  • 896
8

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 ?!

penguin77
  • 1,645
  • 9
  • 8
5

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} *)
LLlAMnYP
  • 11,486
  • 26
  • 65
  • This is throwing an error at me "MapThread called with 5 arguments – penguin77 Apr 15 '15 at 19:38
  • 1
    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 :-) – LLlAMnYP Apr 15 '15 at 19:40
  • 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. – Mr.Wizard Apr 15 '15 at 19:43
  • 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. – LLlAMnYP Apr 15 '15 at 19:47
  • @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. – penguin77 Apr 15 '15 at 19:49
  • @penguin77 I did and corrected it in Mathematica, but forgot to do so in my answer and went shopping. Sorry :-) – LLlAMnYP Apr 15 '15 at 19:49
  • @penguin77 & LL I can't count how many times I've copied the wrong code section into an answer. I suppose it indicates a failure in my developmental methods but I often have similar looking chucks of code for testing different methods and it's often not until the final version that I add proper localization, etc. – Mr.Wizard Apr 15 '15 at 20:17
5

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.

Mr.Wizard
  • 271,378
  • 34
  • 587
  • 1,371
3

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}
m_goldberg
  • 107,779
  • 16
  • 103
  • 257
2
list = {{a, b, c}, {e, f, g}};

If the length of the list elements is known:

Transpose[list] * Range[3] // MapApply[Plus]

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

Otherwise

Transpose[list] * Range[Length @ First @ list] // MapApply[Plus]

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

eldo
  • 67,911
  • 5
  • 60
  • 168
2

Using MapThread:

F = MapThread[Total@*Times, {Transpose@#, Range[Last@Dimensions@#]}] &;

F@{{a, b, c}, {e, f, g}} // RepeatedTiming

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

Also, using MapIndexed:

F = MapIndexed[Total[#1*#2[[1]]] &, Transpose@#] &;

F@{{a, b, c}, {e, f, g}} // RepeatedTiming

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

E. Chan-López
  • 23,117
  • 3
  • 21
  • 44
2

As pointed out in a comment by @eldo (and thanks!)

MapThread[Plus,{{a,b,c},{e,f,g}}] Range[3]//Expand

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

Also

MapThread[Plus]@MapIndexed[#2[[2]]  #1&,{{a,b,c},{e,f,g}},{2}]

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

Original Answer

MapThread[Plus[#1,#2] &,{{a,b,c},{e,f,g}}] Range[3]//Expand

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

user1066
  • 17,923
  • 3
  • 31
  • 49
1

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} *)
ciao
  • 25,774
  • 2
  • 58
  • 139