7
$\begingroup$

Let's say I have

x = {b1, b2}

and

y = {{a1, a2}, {a3, a4}, {a5, a6}}

I want to produce the riffle'd list

z = {{{b1, a1}, {b2, a2}}, {{b1, a3}, {b2, a4}}, {{b1, a5}, {b2, a6}}}

enter image description here

Attempt

I have produced this horible code

riffle[a_, b_] := Riffle[a, b, {1, -1, 2}]
tt = Reverse /@ MapThread[{#1, #2} &, {x, Transpose@y}, 1]; 
Partition[#, 2] & /@ Map[riffle @@ ## &, tt, {1}] // Transpose

which does the trick but...

Question

I am fairly certain there should be a simpler method?

Thank you for your help in seeking elegance in mathematica!

$\endgroup$
  • 6
    $\begingroup$ Why isn't Transpose[{{b1, b2}, #}] & /@ {{a1, a2}, {a3, a4}, {a5, a6}} sufficient? $\endgroup$ – J. M.'s technical difficulties May 13 at 9:19
  • $\begingroup$ @J.M. because you are smart and I am dumb :-). Either write it as an answer or I delete my post? $\endgroup$ – chris May 13 at 9:24
  • 1
    $\begingroup$ I would prefer you answer your own question, if you can explain why it works. ;) $\endgroup$ – J. M.'s technical difficulties May 13 at 10:37
  • $\begingroup$ @J.M. so now I accept my own (i.e. your's) answer? I feel its a bit cheeky! $\endgroup$ – chris May 14 at 9:02
  • $\begingroup$ It would be less cheeky if you added an explanation why it works, you know. ;) (However, I cannot give a second upvote.) $\endgroup$ – J. M.'s technical difficulties May 14 at 9:11
11
$\begingroup$
x = {b1, b2};
y = {{a1, a2}, {a3, a4}, {a5, a6}};

(As suggested in the comments by user1066)

Inner[{#2, #1}&, y, x, List]

$\left( \begin{array}{cc} \{\text{b1},\text{a1}\} & \{\text{b2},\text{a2}\} \\ \{\text{b1},\text{a3}\} & \{\text{b2},\text{a4}\} \\ \{\text{b1},\text{a5}\} & \{\text{b2},\text{a6}\} \\ \end{array} \right)$

My earlier solution:

Inner[Reverse@*List, y, x, List]

Timing comparisons:

i1 = First@RepeatedTiming[ai1 = Inner[{#2, #1} &, y, x, List]];
i2 = First@RepeatedTiming[ai2 = Inner[Reverse@*List, y, x, List]];
t = First@RepeatedTiming[at = Transpose[{x, #}] & /@ y];
{i2, t}/i1

{1.54407, 2.7154}

It gives a 1.5x speed up, and 2.7x compared to using Transpose.

ai1 == ai2 == at

True

| improve this answer | |
$\endgroup$
  • $\begingroup$ +1 Looks elegant. Would you kindly explain how it works? I find it difficult to follow, even after reading about Inner. $\endgroup$ – DavidC May 13 at 13:33
  • 1
    $\begingroup$ @DavidC First evaluate Inner[f, y, x, g]. Then you can better see why the values of g and f are List and Reverse@*List. $\endgroup$ – Suba Thomas May 13 at 13:42
  • $\begingroup$ Actually J. M.'s Transpose solution is simpler and better than this. $\endgroup$ – Suba Thomas May 13 at 13:46
  • $\begingroup$ @David, to put Suba's explanation in another way: think of what happens when you take the usual dot product of a matrix and a vector, and consider what happens if you replace the addition and the multiplication with something else. $\endgroup$ – J. M.'s technical difficulties May 13 at 13:48
  • $\begingroup$ @user1066 Thanks. That speeds it up a little. And it appears that the Inner solution is faster than that using Transpose. $\endgroup$ – Suba Thomas May 13 at 17:34
8
$\begingroup$

as suggested by @J.M. (who gets all the credit) there is in fact a very simple solution (as I feared! :-) ):

  Transpose[{{b1, b2}, #}] & /@ {{a1, a2}, {a3, a4}, {a5, a6}}

Why it works

Well, it seems obvious a posteriori ( :-) ) especially since JM's solution is elegant and simple.

The mapping takes in turn {a1, a2}, {a3, a4} ... and applies {{b1, b2}, #}& to each list so that we have a list of

 {{b1, b2},{a1, a2}},  {{b1, b2},{a3, a4}}...

Thanks to the Transpose it becomes

  {{b1, a1}, {b2, a2}}, {{b1, a3}, {b2, a4}}...

Hence the result.

Postscript

My own clumsy solution arose because I failed to realise that the length of {b1,b2} was always 2.

| improve this answer | |
$\endgroup$
  • $\begingroup$ I use this exact same transpose method to take some list of x values and disperse it to a list of lists of different y values! Very cool. $\endgroup$ – CA Trevillian May 14 at 11:23
4
$\begingroup$

How about something like this?

x = {b1, b2};
y = {{a1, a2}, {a3, a4}, {a5, a6}};
z = {{{b1, a1}, {b2, a2}}, {{b1, a3}, {b2, a4}}, {{b1, a5}, {b2,a6}}};
Partition[#, 2] & /@ Riffle @@@ Table[{x, k}, {k, y}]
%==z

{{{b1, a1}, {b2, a2}}, {{b1, a3}, {b2, a4}}, {{b1, a5}, {b2, a6}}}

True


The first step:

t = Table[{x, k}, {k, y}]

{{{b1, b2}, {a1, a2}}, {{b1, b2}, {a3, a4}}, {{b1, b2}, {a5, a6}}}


The second step:

Riffle@@@ t

{{b1, a1, b2, a2}, {b1, a3, b2, a4}, {b1, a5, b2, a6}}


Then all that's needed is to Partition each sublist. For example,

Partition[{{b1, a1, b2, a2},2]

{{b1,a1},{b2,a2}}

| improve this answer | |
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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