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?

up vote 26 down vote accepted
{{a, b, c}, {1, 2, 3}, {x, y, z}} ~Flatten~ {2, 1}

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

  • Even better! I didn't know Flatten had this more sophisticated use. Plus, this one properly flattens levels. – Andrew Cheong Sep 5 '14 at 15:16
  • 5
    @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.) – Mr.Wizard Sep 5 '14 at 15:25
  • 1
    @Mr.Wizard - Would you consider adding your variant as a separate answer so people more easily see it as an option? – Andrew Cheong Sep 7 '14 at 2:32
  • 1
    @Mr.Wizard Seconding Andrew's request. – Guillochon Jul 24 '15 at 4:28
  • 3
    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}]. – nilo de roock Mar 22 '17 at 19:41

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.

  • 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. – Andrew Cheong Sep 5 '14 at 15:20
Flatten @ Transpose[{{a, b, c}, {1, 2, 3}, {x, y, z}}]

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

  • Ah, of course. Thanks! – Andrew Cheong Sep 5 '14 at 14:40
  • 2
    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. – Andrew Cheong Sep 5 '14 at 15:15
  • @Andrew That case would also be handled by Join @@ – Mr.Wizard Sep 5 '14 at 15:18

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.

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}*)
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}

Your Answer

 
discard

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

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