24
$\begingroup$

I thought Riffle would generalize such that

Riffle[{a,b,c},{1,2,3},{x,y,z}]

would return

{a,1,x,b,2,y,c,3,z}

It turns out that's not the case.

What's the easiest way to do a multi-list riffle?

$\endgroup$

6 Answers 6

31
$\begingroup$
{{a, b, c}, {1, 2, 3}, {x, y, z}} ~Flatten~ {2, 1}

{a, 1, x, b, 2, y, c, 3, z}

$\endgroup$
5
  • $\begingroup$ Even better! I didn't know Flatten had this more sophisticated use. Plus, this one properly flattens levels. $\endgroup$ Commented Sep 5, 2014 at 15:16
  • 5
    $\begingroup$ @Andrew Worth noting is that despite being a single clean operation this is actually slower than Join @@ Transpose @ lists in most cases. (You still have my vote, Simon.) $\endgroup$
    – Mr.Wizard
    Commented Sep 5, 2014 at 15:25
  • 1
    $\begingroup$ @Mr.Wizard - Would you consider adding your variant as a separate answer so people more easily see it as an option? $\endgroup$ Commented Sep 7, 2014 at 2:32
  • 1
    $\begingroup$ @Mr.Wizard Seconding Andrew's request. $\endgroup$
    – Guillochon
    Commented Jul 24, 2015 at 4:28
  • 5
    $\begingroup$ Believe me, {{a, b, c}, {1, 2, 3}, {x, y, z}} ~Flatten~ {2, 1} is newbie unfriendly, compared to Flatten[{{a, b, c}, {1, 2, 3}, {x, y, z}}, {2, 1}]. $\endgroup$ Commented Mar 22, 2017 at 19:41
17
$\begingroup$

Riffle does more than simply interleave lists of equal length. When the lists are not of the same length it will repeat elements from the second list, and it will also leave an element from the first list last position:

Riffle[{a, b, c, d, e, f}, {1, 2, 3}]
{a, 1, b, 2, c, 3, d, 1, e, 2, f}

How this would be generalized to multiple lists is not clear which I suspect is why Riffle permits only two lists as input.

Perhaps you would find use, or at least interest, in the following construct:

multiRiffle[x : _List ..] :=
 Module[{i = 1},
  Fold[Riffle[##, {++i, -1, i}] &, {x}]
 ]

Now:

multiRiffle[{a, b, c}, {1, 2, 3}, {x, y, z}]
{a, 1, x, b, 2, y, c, 3, z}

But also:

multiRiffle[{a, b, c, d}, {1, 2}, {x, y, z}]
{a, 1, x, b, 2, y, c, 1, z, d, 2, x}
multiRiffle[{a, b}, {1, 2, 3, 4}, {x}]
{a, 1, x, b, 2, x}

I think these are closer in spirit to Riffle than a simple transpose/flatten operation.

$\endgroup$
1
  • $\begingroup$ Ah, that makes sense—so it was I who misunderstood Riffle's generalization. Indeed in my application I assume equal-length lists and so @SimonWoods' solution works; however I appreciate the insight into an actually generalized Riffle. $\endgroup$ Commented Sep 5, 2014 at 15:20
16
$\begingroup$
Flatten @ Transpose[{{a, b, c}, {1, 2, 3}, {x, y, z}}]

{a, 1, x, b, 2, y, c, 3, z}

$\endgroup$
3
  • $\begingroup$ Ah, of course. Thanks! $\endgroup$ Commented Sep 5, 2014 at 14:40
  • 2
    $\begingroup$ For future readers: use Flatten[Transpose[{{a, b, c}, {1, 2, 3}, {x, y, z}}], 1] if your elements are lists themselves, or else they'll be flattened too. $\endgroup$ Commented Sep 5, 2014 at 15:15
  • $\begingroup$ @Andrew That case would also be handled by Join @@ $\endgroup$
    – Mr.Wizard
    Commented Sep 5, 2014 at 15:18
7
$\begingroup$

Two people asked me to re-post a comment as a separate answer for greater visibility.

Regarding Simon's Flatten method I commented:

Worth noting is that despite being a single clean operation this is actually slower than Join @@ Transpose @ lists in most cases.

Part of the reason that I "only" commented is that I had not invested the time necessary to back up this claim, and as we will see it is questionable. Here are the two functions I shall be comparing and the test function I shall use:

f1 = # ~Flatten~ {2, 1} &;
f2 = Join @@ (#\[Transpose]) &;

test = Table[First @ RepeatedTiming @ fn[#], {fn, {f1, f2}}] &;

All timings are being performed in version 10.1.0 under Windows x64.

First with packed integers with three different array shapes:

RandomInteger[99, {50000, 5}] // test
RandomInteger[99, {5, 50000}] // test
RandomInteger[99, {500, 500}] // test
{0.00200, 0.00115}

{0.00209, 0.0127}

{0.00205, 0.00164}

So f1 (Flatten) is uniform in speed whereas f2 varies with the shape of the array. In the case where input is only a few very long rows its is significantly slower than f1, but in the other two cases it does pull ahead.

With unpackable data:

RandomChoice[{"a", "b", "c"}, {50000, 5}] // test
RandomChoice[{"a", "b", "c"}, {5, 50000}] // test
RandomChoice[{"a", "b", "c"}, {500, 500}] // test
{0.00788, 0.00558}

{0.00626, 0.00774}

{0.00684, 0.00359}

both functions slow down with the exception of f2 on the long-rows case where it is actually faster (but still slower than f1 on the same data). Timings are fairly similar in the first two cases but with the square matrix f2 is nearly twice as fast.

I conclude that my statement that f2 is faster "in most cases" is dubitable, however f2 is enough faster in some cases that it is still worth knowing.

$\endgroup$
4
$\begingroup$

More genaral approach:

Flatten@Riffle[{a, b, c, d, e, f, g}, Transpose@{{1, 2, 3}, {x, y, z}}]
(*{a, 1, x, b, 2, y, c, 3, z, d, 1, x, e, 2, y, f, 3, z, g}*)
$\endgroup$
4
$\begingroup$
MapThread[Sequence, {{a, b, c}, {1, 2, 3}, {x, y, z}}]

{a, 1, x, b, 2, y, c, 3, z}

Thread[Unevaluated @ Sequence[{a, b, c}, {1, 2, 3}, {x, y, z}]]

{a, 1, x, b, 2, y, c, 3, z}

$\endgroup$

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