Wolfram Research

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

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

Scope

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[5]=

The final results are ragged:

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

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

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

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

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

Create an operator of GeneralizedMapThread:

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

Use the operator on some data:

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

Applications

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

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

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

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

This can also be achieved using Through:

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

Properties and Relations

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

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

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

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

GeneralizedMapThread can handle these cases:

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

Thread can handle arrays with different depths:

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

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

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

Possible Issues

Associations must have the same keys:

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

Associations and lists cannot be mixed:

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

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

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

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[22]:=
ResourceFunction["GeneralizedMapThread"][f, {{1, 2}, {2, 3, 4}}]
Out[22]=

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

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

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

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

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

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

Neat Examples

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

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

Create a complex ragged array with different depths:

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

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

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

Resource History

License Information