14
$\begingroup$

I want a function that takes in a number $n_0$ and a list $\{n_1, n_2, n_3, n_4, n_5\}$ and returns $\{\{n_0,n_1\},\{n_0,n_2\}, \{n_0,n_3\},... \}$. The reason I want this is to use it in Map, so I don't want to need to define a variable, which would require Module[] or the like.

Here's my attempt:

({#1,#2}&/@#2)&[n0,{n1,n2,n3,n4,n5}]

But this obviously won't work, since the leftmost #2 is nonexistent (the Map iterates over the list {n1 n2 n3 ...}, so #1 is one of n1, n2, ..., and #2 is not defined anymore). It seems no matter what you do, it's not possible.

Unless you propose a solution I'm not seeing, it seems Mathematica is very weak on this front. Many other languages support the naming of the variables in anonymous functions which would easily resolve this issue.

It would be extremely helpful if Map had a 3rd input which let us specify constants that get fed into the function in #2 and #3..., which is something other languages also do. The fact that none of this is possible is very strange to me.

Help! This is my #1 problem in Mathematica (no pun intended).

EDIT: The notation a|->f[a] and {a,b,...}|->f[a,b,...] let you define functions using variable names which fixes my issue and is extremely helpful in general. It's a bit scary to me how many people in the answers abuse these insane functions when they could just do something simple, but we got a couple good answers too, so definitely give them a read if you want (or dont :P) aight peace migos u boiz bin crazy dilly dawg, see dem dawgz on da flip size

$\endgroup$

7 Answers 7

21
$\begingroup$
f = Thread @* List;

f[n0, {n1, n2, n3, n4, n5}]
{{n0, n1}, {n0, n2}, {n0, n3}, {n0, n4}, {n0, n5}}

If you have to use Map and pure functions, you can have pure functions with named parameters:

({x, y} |-> (z |-> {x, z}) /@ y)[n0, {n1, n2, n3, n4, n5}]
{{n0, n1}, {n0, n2}, {n0, n3}, {n0, n4}, {n0, n5}}

Alternatively,

Function[{x, y}, Function[z, {x, z}] /@ y][n0, {n1, n2, n3, n4, n5}]
{{n0, n1}, {n0, n2}, {n0, n3}, {n0, n4}, {n0, n5}}

You can also mix Slots and named parameters:

(x |-> {#1, x}) /@ #2 &[n0, {n1, n2, n3, n4, n5}]
{{n0, n1}, {n0, n2}, {n0, n3}, {n0, n4}, {n0, n5}}

or

({x, y} |-> {x, #} & /@ y)[n0, {n1, n2, n3, n4, n5}]
{{n0, n1}, {n0, n2}, {n0, n3}, {n0, n4}, {n0, n5}}
$\endgroup$
2
  • 1
    $\begingroup$ This shorthand in Mathematica never ceases to amaze me! $\endgroup$
    – rowsi
    Feb 3, 2022 at 8:55
  • 1
    $\begingroup$ This is the syntax I needed. Didn't realize you could name function variables. Thanks! All the other answers are correct, but they use unnecessary functions that overcomplicate in my opinion. $\endgroup$ Feb 3, 2022 at 9:12
14
$\begingroup$

Best on packed arrays...

PadLeft[Transpose@{#2}, {Automatic, 2}, {#1}] &[
  n0, {n1, n2, n3, n4, n5}]

(*  {{n0, n1}, {n0, n2}, {n0, n3}, {n0, n4}, {n0, n5}}  *)

PadLeft[ArrayReshape[#2, {Length@#2, 1}], {Automatic, 2}, {#1}] &[
  n0, {n1, n2, n3, n4, n5}]

(*  {{n0, n1}, {n0, n2}, {n0, n3}, {n0, n4}, {n0, n5}}  *)

...except for this modification of @Syed's first solution:

First@Outer[List, Developer`ToPackedArray@{#1}, #2]&[
  [n0, {n1, n2, n3, n4, n5}]

Note on performance, just because this stuff is hidden everywhere and I feel like sharing it in one more spot:

Visitors to this Q&A may have the same problem as in the OP, but are performing the operation on large packed data arrays, and wish to avoid a performance hit. The above is meant to complement the other answers. To keep data packed, you have to make use of functions or certain forms of calling functions that keep data packed. On large data Map will Compile the function if possible, in which case the data will stay packed; but usually Map is not as efficient as array-processing functions like PadLeft, PadRight, ArrayReshape, and Transpose. When data is symbolic or unpacked numeric, then these methods are usually efficient but not more efficient than Thread or Map; IIRC, they can be worse.†

For instance, in relation to the Outer solution,

Outer[f, packed1, packed2]

when f is List, Plus or Times (only), are three of the most efficiently implemented function calls in Mathematica, all just as fast as

ConstantArray[1., {Length@packed1, Length@packed2}]

even though f = Plus and f = Times do arithmetic. (I believe it's called "pipelining," what happens in the CPU.) However,

Outer[f, unpacked1, packed2]

results in an unpacked array for output and is much slower. That's why Developer`ToPackedArray@{#1} is needed to realize the potential efficiency of Outer. And when f is not one of the three functions, Outer and Table are often similar in speed.

Anyway, that's a little explanation of why the mod to @Syed's solution is so fast, about four times faster than the two array-processing solutions given by me at the beginning, which themselves are three times faster than compiled-Map solutions and much faster than the ones that unpack the data.

†Footnote: An unpacked matrix is a list of lists, which is really a list of pointers to lists; a packed matrix, which structurally resembles a list of lists, has its data stored in a 2D C array. Some functions are more efficient on one data structure than the other; some have better complexity as the size of the data grows. It is possible for differences in performance occasionally to narrow from version to version, if heuristics for picking optimal internal code are improved. I'm still figuring out exactly how my switch to Apple M1 has affected the MKL.

††I kinda got carried away this morning. I can remove the addendum if it's too long. :)

$\endgroup$
1
  • $\begingroup$ Thank you for the detailed explanation and the mod. Much appreciated. $\endgroup$
    – Syed
    Feb 4, 2022 at 5:23
9
$\begingroup$
First@Outer[List, {n0}, {n1, n2, n3, n4, n5}]

or

{n0, #} & /@ {n1, n2, n3, n4, n5}

or

Using Sow/Reap

First@Last@Reap@Scan[Sow[{n0, #}] &, {n1, n2, n3, n4, n5}]

Result

{{n0, n1}, {n0, n2}, {n0, n3}, {n0, n4}, {n0, n5}}

$\endgroup$
1
  • 1
    $\begingroup$ First@Outer[List, Developer`ToPackedArray@{n0}, list] is much better when list is packed and n0 is of the same type. $\endgroup$
    – Michael E2
    Feb 3, 2022 at 16:46
8
$\begingroup$

You could try Riffle and Partition for this. Input:

list = {n1, n2, n3, n4, n5};
Partition[Riffle[list, n0, {1, -1, 2}], 2]

Returns:

{{n0, n1}, {n0, n2}, {n0, n3}, {n0, n4}, {n0, n5}}
$\endgroup$
5
$\begingroup$
ClearAll[fn];
SetAttributes[fn, Orderless];

fn[a_, b_List] := Tuples[{{a}, b}];

Used Orderless to handle cases when the first argument is a List.

fn[n0, {n1, n2, n3, n4, n5}]

(* or *)

fn[{n1, n2, n3, n4, n5}, n0]

(* Out: {{n0, n1}, {n0, n2}, {n0, n3}, {n0, n4}, {n0, n5}} *)
$\endgroup$
4
$\begingroup$
Distribute[{n0, {n1, n2, n3, n4, n5}},List]

(* {{n0, n1}, {n0, n2}, {n0, n3}, {n0, n4}, {n0, n5}} *)
$\endgroup$
3
$\begingroup$

Other way:

   MyList[var_, n_] := Block[{array1, array2, mylist},
   array1 := Array[ToExpression[ToString[var] <> ToString[#]] &, 1, 0][[1]];
   array2 := Array[ToExpression[ToString[var] <> ToString[#]] &, n];
   mylist := Outer[List[array1, #] &, array2];
   Return[mylist];
   ];

Test:

MyList[n,5]
(*{{n0, n1}, {n0, n2}, {n0, n3}, {n0, n4}, {n0, n5}}*)

MyList[s,8]
(*{{s0, s1}, {s0, s2}, {s0, s3}, {s0, s4}, {s0, s5}, {s0, s6}, {s0, s7}, {s0, s8}}*)
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.