# Thread over uneven (ragged) expressions with full functionality?

There are a number of questions on this site about "threading" including several relating to uneven (ragged) lists. However, none that I could find deal with the full functionality of Thread but instead things like MapThread, or the ragged transpose of Flatten[expr, {2}].

Thread operates:

• Inside held expressions without errant evaluation

• On arbitrary heads as specified with the second parameter

• By distributing singlets across all subexpressions

• On only the first n arguments (subexpressions) if given as third parameter

For example:

expr = Hold[qq[2 + 2, 1/0, 8/4], qq[1, 2, 3], Sqrt[2 + 2]];

Thread[expr, qq]

Thread[expr, qq, 1]

qq[Hold[2 + 2, 1, Sqrt[2 + 2]], Hold[1/0, 2, Sqrt[2 + 2]], Hold[8/4, 3, Sqrt[2 + 2]]]

qq[Hold[2 + 2, qq[1, 2, 3], Sqrt[2 + 2]], Hold[1/0, qq[1, 2, 3], Sqrt[2 + 2]],
Hold[8/4, qq[1, 2, 3], Sqrt[2 + 2]]]


All of this breaks if we change qq[1, 2, 3] to qq[1, 2].

What is the most efficient way to extend Thread to ragged subexpressions?

## 3 Answers

This is my take on the pad, thread normally, then remove padding approach.

fred[expr_, head_: List, seq_: All] :=
Module[{myhold, maxlength, dummy, paddedexpr},
SetAttributes[myhold, HoldAllComplete];
maxlength = Max@Cases[expr, head[args___] :> Length@Hold@args, {1}];
paddedexpr = Replace[expr, head[args___] :>
RuleCondition@PadRight[myhold@args, maxlength, dummy], {1}];
DeleteCases[Thread[paddedexpr, myhold, seq], dummy, -1] /. myhold -> head]

• Much more polished :) I didn't know Pad.. worked on arbitrary heads. Sweet. Jun 9, 2014 at 22:05
• +1, I would have done something quite similar. I think the Replace level better be {1} in case the whole expression is wrapped in head.
– Rojo
Jun 11, 2014 at 0:53
• Also, there's the small issue of definitions that didn't match now matching with the padded dummy arguments. Perhaps keeping the myhold and some other one for the head of heads, and replacing them back after the threading? Not sure if I am thinking straight, and I don't have MMA to try stuff
– Rojo
Jun 11, 2014 at 0:53
• @Rojo, thanks - good points. Jun 11, 2014 at 8:34

// timidly raises hand

Maybe this approach?

ClearAll[Thread2];
SetAttributes[Thread2, HoldAllComplete];
Thread2[expr_, etc___] :=
DeleteCases[
Thread[
With[
{max = Length /@ {expr} // Max},
Quiet[
Replace[
expr,
h_[c___] /; 1 < Length@{c} < max :>
RuleCondition[
h[c,
Sequence @@ ConstantArray[EatMe, max - Length@{c}]]],
{1}]]], etc],
EatMe, 3];

expr = Hold[qq[2 + 2, 1/0, 8/4], qq[1, 2, 3], Sqrt[2 + 2]];
Thread2[expr, qq]
Thread2[expr, qq, 1]

qq[Hold[2 + 2, 1, Sqrt[2 + 2]], Hold[1/0, 2, Sqrt[2 + 2]], Hold[8/4, 3, Sqrt[2 + 2]]]

qq[Hold[2 + 2, qq[1, 2, 3], Sqrt[2 + 2]], Hold[1/0, qq[1, 2, 3], Sqrt[2 + 2]],  Hold[8/4, qq[1, 2, 3], Sqrt[2 + 2]]]

expr2 = Hold[qq[2 + 2, 1/0, 8/4], qq[1, 2], Sqrt[2 + 2]];
Thread2[expr2, qq]
Thread2[expr2, qq, 1]

qq[Hold[2 + 2, 1, Sqrt[2 + 2]], Hold[1/0, 2, Sqrt[2 + 2]], Hold[8/4, Sqrt[2 + 2]]]

qq[Hold[2 + 2, qq[1, 2], Sqrt[2 + 2]], Hold[1/0, qq[1, 2], Sqrt[2 + 2]], Hold[8/4, qq[1, 2], Sqrt[2 + 2]]]

• There is no need to be timid. Thanks for answering. I had my own ideas for implementing this but I wasn't feeling very imaginative so I wanted to see how others would do it. Jun 9, 2014 at 7:30
• Thread2[{{1, 2, 3, 4, 5}, {6, 7, 8}}] gives a Thread::tdlen error. Jun 9, 2014 at 12:43

If I understand the question correctly, you are looking to use Thread on ragged subexpressions. I will give my solution on ragged list:

list = {{a, b}, {c, f, r, t}, {t, y, k}};
length = Length[#] & /@ list;
max = Max@length;
list2 = Join[#, ConstantArray[0, max - Length[#]]] & /@ list;
list3 = Thread[List @@ list2];
list4 = DeleteCases[list3, _ 0, \[Infinity]];
f @@@ list4

(*    {f[a, c, t], f[b, f, y], f[r, k], f[t]}*)

• Is this intended to be only an illustration? Will this handle the additional parameters of Thread? I think you would also need to avoid accidentally deleting zeros that were present in the original expression. By the way, you don't need to write Length[#] & /@ list -- Length is already a function, so write: Length /@ list. :-) Jun 9, 2014 at 7:34
• @Mr. Wizard, thank you for your input. regarding deleting zero, this can be changed to be any value that is not showing in the ragged list. Jun 9, 2014 at 15:29