Function Repository Resource:

GeneralizedMapThread

Source Notebook

A version of MapThread that allows for ragged arrays and for lists with unequal depth

Contributed by: Sander Huisman

ResourceFunction["GeneralizedMapThread"][f,{{a1,a2,},{b1,b2,},}]

gives {f[a1,b1,],f[a2,b2,],}.

ResourceFunction["GeneralizedMapThread"][f,{expr1,expr2,},n]

applies f to the parts of the expri at level n.

ResourceFunction["GeneralizedMapThread"][f]

represents an operator form of ResourceFunction["GeneralizedMapThread"] that can be applied to an expression.

If the lists do not have equal length, the smallest lists are made equal length by making copies using ConstantArray.
ResourceFunction["GeneralizedMapThread"] works on Association objects.
ResourceFunction["GeneralizedMapThread"][f][expr] is equivalent to ResourceFunction["GeneralizedMapThread"][f, expr].

Examples

Basic Examples (3) 

Have the second argument be a constant:

In[1]:=
ResourceFunction[
 "GeneralizedMapThread"][f, {{1, 2, 3, 4}, 9, {a, b, c, d}}]
Out[1]=

It works on expressions with unequal lengths ("ragged"):

In[2]:=
ResourceFunction[
 "GeneralizedMapThread"][f, {{Range[2], Range[3], Range[4]}, {{a, b}, {c, d, e}, {f, g, h, i}}}, 2]
Out[2]=

Apply f to the corresponding values of associations, copying values where necessary:

In[3]:=
ResourceFunction[
 "GeneralizedMapThread"][f, {<|a -> {1, 2}|>, <|a -> 2|>, <|
   a -> {3, 4}|>}, 2]
Out[3]=

Apply to a list of lists with varying depths and lengths:

In[4]:=
mid = {Range[3], Range[4], Range[5], Range[2]};
out = ResourceFunction["GeneralizedMapThread"][f, {Range[4], mid, 4}, 2]
Out[4]=

The final results are ragged:

In[5]:=
Grid[out]
Out[5]=

Apply at level 1 means that {a,b} from the final list is considered a single value:

In[6]:=
ResourceFunction[
 "GeneralizedMapThread"][f, {{11, 12, 13}, 2, {7, 8, {a, b}}}]
Out[6]=

Apply at level 2 means interpreting {a,b} as separate values, and copying all the other values:

In[7]:=
ResourceFunction[
 "GeneralizedMapThread"][f, {{11, 12, 13}, 2, {7, 8, {a, b}}}, 2]
Out[7]=

Create an operator of GeneralizedMapThread:

In[8]:=
op = ResourceFunction["GeneralizedMapThread"][f];

Use the operator on some data:

In[9]:=
op[{{1, 2, 3}, 5}]
Out[9]=

Subtract the vector {a,b} from a list of vectors:

In[10]:=
ResourceFunction["GeneralizedMapThread"][
 Subtract[#1, First@#2] &, {{{1, 1}, {1, 2}, {2, 3}, {4, 5}}, c[{a, b}]}]
Out[10]=

Apply a list of functions to a single argument without pure functions:

In[11]:=
ResourceFunction["GeneralizedMapThread"][Construct, {{f, g, h}, 2}]
Out[11]=

This can also be achieved using Through:

In[12]:=
Through[{f, g, h}[2]]
Out[12]=

When the arrays are of equal length, MapThread and GeneralizedMapThread give the same result:

In[13]:=
ResourceFunction["GeneralizedMapThread"][
  f, {{11, 12, 13}, {1, 2, 3}, {7, 8, 9}}] === MapThread[f, {{11, 12, 13}, {1, 2, 3}, {7, 8, 9}}]
Out[13]=

MapThread cannot handle lists with elements of unequal length ("ragged"):

In[14]:=
MapThread[f, {{{1, 2}, {3, 4, 5}}, {{a, b}, {c, d, e}}}, 2]
Out[14]=

GeneralizedMapThread can handle these cases:

In[15]:=
ResourceFunction[
 "GeneralizedMapThread"][f, {{{1, 2}, {3, 4, 5}}, {{a, b}, {c, d, e}}}, 2]
Out[15]=

Thread can handle arrays with different depths:

In[16]:=
Thread[f[{1, 2, 3}, 2, {4, 5, 6}]]
Out[16]=

GeneralizedMapThread can also do this but the syntax of GeneralizedMapThread follows the syntax of MapThread:

In[17]:=
ResourceFunction["GeneralizedMapThread"][f, {{1, 2, 3}, 2, {4, 5, 6}}]
Out[17]=

Associations must have the same keys:

In[18]:=
ResourceFunction[
 "GeneralizedMapThread"][f, {<|a -> 2|>, <|a -> 3|>, <|a -> 4, b -> 2|>}]
Out[18]=

Associations and lists can not be mixed:

In[19]:=
ResourceFunction["GeneralizedMapThread"][f, {<|a -> 1|>, 2}, 1]
Out[19]=

Values with another head, such as List or Association, or an incorrect length will get copied:

In[20]:=
ResourceFunction["GeneralizedMapThread"][f, {{1, 2}, G[a, b]}]
Out[20]=

For the case of unequal length, the longest list is taken as the actual length. Here the last list is the longest; the first list is copied:

In[21]:=
ResourceFunction["GeneralizedMapThread"][f, {{1, 2}, {2, 3, 4}}]
Out[21]=

Here the first list is the longest; the last list is copied:

In[22]:=
ResourceFunction["GeneralizedMapThread"][f, {{1, 2}, {2}}]
Out[22]=

An extra bracket around the second list will make it the shortest:

In[23]:=
ResourceFunction["GeneralizedMapThread"][f, {{1, 2}, {{2, 3, 4}}}]
Out[23]=

One can also give it a different head, which prevent it from being interpreted as a list:

In[24]:=
ResourceFunction["GeneralizedMapThread"][f, {{1, 2}, c[2, 3, 4]}, 1]
Out[24]=

Interpret the entries as three levels deep, copying them all:

In[25]:=
ResourceFunction["GeneralizedMapThread"][f, {1, 2, 3}, 3]
Out[25]=

Create a complex ragged array with different depths:

In[26]:=
SeedRandom[1234];
random = (RandomInteger[5, #] & /@ RandomInteger[{2, 6}, {#, 3}]) & /@
   RandomInteger[{1, 6}, 2]
Out[26]=

Apply the function f five levels deep, with the second argument set constant to a:

In[27]:=
ResourceFunction["GeneralizedMapThread"][f, {random, a}, 5]
Out[27]=

Publisher

SHuisman

Version History

  • 1.0.0 – 02 August 2019

License Information