MapThread works fine and dandy with rectangular list structures:

MapThread[f, {{{a, b}, {c, d}}, {{1, 2}, {3, 4}}}, 2]


{{f[a, 1], f[b, 2]}, {f[c, 3], f[d, 4]}}

But with a ragged structure, it starts complaining:

MapThread[f, {{{a, b}, {c, d, e}}, {{1, 2}, {3, 4, 5}}}, 2]


MapThread::mptd: "Object {{a,b},{c,d,e}} at position {2, 1} in MapThread[f,{{{a,b},{c,d,e}},{{1,2},{3,4,5}}},2] has only 1 of required 2 dimensions."

whereas I'd like:

{{f[a, 1], f[b, 2]}, {f[c, 3], f[d, 4], f[e, 5]}}

I can't see any obvious way to achieve a pairing of the corresponding elements, but maybe you can?

Perhaps something like this, as a more general alternative? However, without tweaking it forget about level specification

Function[Null, f[##], Listable] @@ A

• The magic of Listable! I knew there must have been some nice way to do it. May 28, 2012 at 4:27
• @wxffles I like it. If you actually want to give the arguments as separate lists like in your solution, you don't even need the Apply
– Rojo
May 28, 2012 at 4:37
• This is crazy amazing! MMA is effin' nuts in its state space a.e.! Nov 1, 2022 at 14:09

Here's a way to do it by mapping MapThread:

MapThread[f, #] & /@ Transpose[{{{a, b}, {c, d, e}}, {{1, 2}, {3, 4, 5}}}]
(* {{f[a, 1], f[b, 2]}, {f[c, 3], f[d, 4], f[e, 5]}} *)

• This works great if you are working at level 2. I was hoping for something more general. Next time I'll try asking! May 28, 2012 at 3:42
• @wxffles please provide more examples if you want a more general solution. May 28, 2012 at 3:47

It's probably bad form to answer your own question, but I did manage to get something to work while I was waiting:

myMapThread[f_, list1_, list2_, level_] :=
Module[{s}, Function[s,
Reap[MapIndexed[Sow[f[#1, s[[Sequence @@ #2]]]] &, list1, {level}]][[1]]]
[list2]];


Usage:

myMapThread[f, {{a, b}, {c, d, e}}, {{1, 2}, {3, 4, 5}}, 2]


{{f[a, 1], f[b, 2]}, {f[c, 3], f[d, 4], f[e, 5]}}

It's quite ugly though.

• It's not bad form, in fact it is actively encouraged. May 28, 2012 at 3:51
ClearAll[raggedThread]


Example:

raggedThread[f, {{a, b}, {c, d, e}}, {{1, 2}, {3, 4, 5}}]

{{f[a, 1], f[b, 2]}, {f[c, 3], f[d, 4], f[e, 5]}}

• Necromancer badge :-) Jun 13, 2020 at 13:38
• Thank you @Mr.Wizard.
– kglr
Jun 13, 2020 at 16:03
MapThread[f, lst[[1 ;; 2, #]]] & /@ {1, 2}


gives

(*{{f[a, 1], f[b, 2]}, {f[c, 3], f[d, 4], f[e, 5]}}*)


If

lst2 = {{{a, b}, {c, d, e}}, {{1, 2}, {3, 4, 5}}, {{aa, bb}, {cc, dd, ee}}};
MapThread[f, lst2[[1 ;; 3, #]]] & /@ {1, 2}


gives

(*{{f[a, 1, aa], f[b, 2, bb]}, {f[c, 3, cc], f[d, 4, dd], f[e, 5, ee]}}*)

x = {{a, b}, {c, d, e}};
y = {{1, 2}, {3, 4, 5}};

MapApply[f] /@ Transpose /@ Transpose[{x, y}]


{{f[a, 1], f[b, 2]}, {f[c, 3], f[d, 4], f[e, 5]}}

threadRagged = Apply[#, Flatten[{##2}, {{2}, {3}}], {2}] &;


Examples:

{x, y} = {{{a, b}, {c, d, e}}, y = {{1, 2}, {3, 4, 5}}};


{{f[a, 1], f[b, 2]},
{f[c, 3], f[d, 4], f[e, 5]}}

lst2 = {{{a, b}, {c, d, e}}, {{1, 2}, {3, 4, 5}}, {{aa, bb}, {cc, dd, ee}}};


 {{F[a, 1, aa], F[b, 2, bb]},
{F[c, 3, cc], F[d, 4, dd], F[e, 5, ee]}}

threadTwice = Map[Thread]@Thread[#@##2, List, Length @ {##2}] &;


Examples:

{x, y} = {{{a, b}, {c, d, e}}, y = {{1, 2}, {3, 4, 5}}};


 {{f[a, 1], f[b, 2]},
{f[c, 3], f[d, 4], f[e, 5]}}

lst2 = {{{a, b}, {c, d, e}}, {{1, 2}, {3, 4, 5}}, {{aa, bb}, {cc, dd, ee}}};


{{f[a, 1, aa], f[b, 2, bb]},
{f[c, 3, cc], f[d, 4, dd], f[e, 5, ee]}}


Using the third argument of GroupBy to cover the ragged array case:

myMapThread[f_, list_] :=
If[TensorQ[list], f @@@ # &@*Thread /@ Transpose@list,
Values[GroupBy[Catenate[list], Length, f @@@ # &@*Thread]]]


Testing myMapThread:

list1 = {{{a, b}, {c, d}}, {{1, 2}, {3, 4}}};
list2 = {{{a, b}, {c, d, e}}, {{1, 2}, {3, 4, 5}}};