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.

Details and Options

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]=
Image

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]=
Image

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]=
Image

Scope (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]=
Image

The final results are ragged:

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

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]=
Image

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]=
Image

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]=
Image

Applications (2) 

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]=
Image

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

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

This can also be achieved using Through:

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

Properties and Relations (3) 

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]=
Image

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]
Image
Out[14]=
Image

GeneralizedMapThread can handle these cases:

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

Thread can handle arrays with different depths:

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

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]=
Image

Possible Issues (4) 

Associations must have the same keys:

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

Associations and lists can not be mixed:

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

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]=
Image

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]=
Image

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

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

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

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

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]=
Image

Neat Examples (2) 

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

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

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]=
Image

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]=
Image

Publisher

SHuisman

Version History

  • 1.0.0 – 02 August 2019

License Information