reduce array of arrays into on array in collated order












9















I'm trying to use reduce() combine a set of arrays in a "collated" order so items with similar indexes are together. For example:



input = [["one","two","three"],["uno","dos"],["1","2","3","4"],["first","second","third"]]

output = [ 'first','1','uno','one','second','2','dos','two','third','3','three','4' ]


It doesn't matter what order the items with similar index go as long as they are together, so a result of 'one','uno','1'... is a good as what's above. I would like to do it just using immutable variables if possible.



I have a way that works:



    const output = input.reduce((accumulator, currentArray, arrayIndex)=>{
currentArray.forEach((item,itemIndex)=>{
const newIndex = itemIndex*(arrayIndex+1);
accumulator.splice(newIndex<accumulator.length?newIndex:accumulator.length,0,item);
})
return accumulator;
})


But it's not very pretty and I don't like it, especially because of the way it mutates the accumulator in the forEach method. I feel there must be a more elegant method.



I can't believe no one has asked this before but I've tried a bunch of different queries and can't find it, so kindly tell me if it's there and I missed it. Is there a better way?



To clarify per question in comments, I would like to be able to do this without mutating any variables or arrays as I'm doing with the accumulator.splice and to only use functional methods such as .map, or .reduce not a mutating loop like a .forEach.










share|improve this question

























  • "Is there a better way?" What do you mean by "better"? "I don't like it" is not an objective coding problem. What is the requirement?

    – guest271314
    8 hours ago













  • Well for one thing I'd like to do it without mutating any variables, or introducing a .forEach loop which isn't really functional. And I think it can be done much more concisely as well. If I could zip a set of arrays together like in Python then reduce(concat) them together that way would work too.

    – jimboweb
    8 hours ago











  • I think I pretty clearly said I'd like the result to not mutate any arrays or variables, as I am doing with the accumulator.splice. That is an objective requirement.

    – jimboweb
    8 hours ago











  • Does the requirement include a restriction on using JSON.parse(JSON.stringify(input)) to avoid mutating the original array?

    – guest271314
    7 hours ago













  • Wow, lots of great answers, thanks. I'm a little torn on which one to accept as a solution, so I have to try them out. I'll choose a solution in a day or so. but I voted you all up. Thanks again.

    – jimboweb
    4 hours ago
















9















I'm trying to use reduce() combine a set of arrays in a "collated" order so items with similar indexes are together. For example:



input = [["one","two","three"],["uno","dos"],["1","2","3","4"],["first","second","third"]]

output = [ 'first','1','uno','one','second','2','dos','two','third','3','three','4' ]


It doesn't matter what order the items with similar index go as long as they are together, so a result of 'one','uno','1'... is a good as what's above. I would like to do it just using immutable variables if possible.



I have a way that works:



    const output = input.reduce((accumulator, currentArray, arrayIndex)=>{
currentArray.forEach((item,itemIndex)=>{
const newIndex = itemIndex*(arrayIndex+1);
accumulator.splice(newIndex<accumulator.length?newIndex:accumulator.length,0,item);
})
return accumulator;
})


But it's not very pretty and I don't like it, especially because of the way it mutates the accumulator in the forEach method. I feel there must be a more elegant method.



I can't believe no one has asked this before but I've tried a bunch of different queries and can't find it, so kindly tell me if it's there and I missed it. Is there a better way?



To clarify per question in comments, I would like to be able to do this without mutating any variables or arrays as I'm doing with the accumulator.splice and to only use functional methods such as .map, or .reduce not a mutating loop like a .forEach.










share|improve this question

























  • "Is there a better way?" What do you mean by "better"? "I don't like it" is not an objective coding problem. What is the requirement?

    – guest271314
    8 hours ago













  • Well for one thing I'd like to do it without mutating any variables, or introducing a .forEach loop which isn't really functional. And I think it can be done much more concisely as well. If I could zip a set of arrays together like in Python then reduce(concat) them together that way would work too.

    – jimboweb
    8 hours ago











  • I think I pretty clearly said I'd like the result to not mutate any arrays or variables, as I am doing with the accumulator.splice. That is an objective requirement.

    – jimboweb
    8 hours ago











  • Does the requirement include a restriction on using JSON.parse(JSON.stringify(input)) to avoid mutating the original array?

    – guest271314
    7 hours ago













  • Wow, lots of great answers, thanks. I'm a little torn on which one to accept as a solution, so I have to try them out. I'll choose a solution in a day or so. but I voted you all up. Thanks again.

    – jimboweb
    4 hours ago














9












9








9


2






I'm trying to use reduce() combine a set of arrays in a "collated" order so items with similar indexes are together. For example:



input = [["one","two","three"],["uno","dos"],["1","2","3","4"],["first","second","third"]]

output = [ 'first','1','uno','one','second','2','dos','two','third','3','three','4' ]


It doesn't matter what order the items with similar index go as long as they are together, so a result of 'one','uno','1'... is a good as what's above. I would like to do it just using immutable variables if possible.



I have a way that works:



    const output = input.reduce((accumulator, currentArray, arrayIndex)=>{
currentArray.forEach((item,itemIndex)=>{
const newIndex = itemIndex*(arrayIndex+1);
accumulator.splice(newIndex<accumulator.length?newIndex:accumulator.length,0,item);
})
return accumulator;
})


But it's not very pretty and I don't like it, especially because of the way it mutates the accumulator in the forEach method. I feel there must be a more elegant method.



I can't believe no one has asked this before but I've tried a bunch of different queries and can't find it, so kindly tell me if it's there and I missed it. Is there a better way?



To clarify per question in comments, I would like to be able to do this without mutating any variables or arrays as I'm doing with the accumulator.splice and to only use functional methods such as .map, or .reduce not a mutating loop like a .forEach.










share|improve this question
















I'm trying to use reduce() combine a set of arrays in a "collated" order so items with similar indexes are together. For example:



input = [["one","two","three"],["uno","dos"],["1","2","3","4"],["first","second","third"]]

output = [ 'first','1','uno','one','second','2','dos','two','third','3','three','4' ]


It doesn't matter what order the items with similar index go as long as they are together, so a result of 'one','uno','1'... is a good as what's above. I would like to do it just using immutable variables if possible.



I have a way that works:



    const output = input.reduce((accumulator, currentArray, arrayIndex)=>{
currentArray.forEach((item,itemIndex)=>{
const newIndex = itemIndex*(arrayIndex+1);
accumulator.splice(newIndex<accumulator.length?newIndex:accumulator.length,0,item);
})
return accumulator;
})


But it's not very pretty and I don't like it, especially because of the way it mutates the accumulator in the forEach method. I feel there must be a more elegant method.



I can't believe no one has asked this before but I've tried a bunch of different queries and can't find it, so kindly tell me if it's there and I missed it. Is there a better way?



To clarify per question in comments, I would like to be able to do this without mutating any variables or arrays as I'm doing with the accumulator.splice and to only use functional methods such as .map, or .reduce not a mutating loop like a .forEach.







javascript arrays functional-programming






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited 8 hours ago







jimboweb

















asked 8 hours ago









jimbowebjimboweb

1,75611027




1,75611027













  • "Is there a better way?" What do you mean by "better"? "I don't like it" is not an objective coding problem. What is the requirement?

    – guest271314
    8 hours ago













  • Well for one thing I'd like to do it without mutating any variables, or introducing a .forEach loop which isn't really functional. And I think it can be done much more concisely as well. If I could zip a set of arrays together like in Python then reduce(concat) them together that way would work too.

    – jimboweb
    8 hours ago











  • I think I pretty clearly said I'd like the result to not mutate any arrays or variables, as I am doing with the accumulator.splice. That is an objective requirement.

    – jimboweb
    8 hours ago











  • Does the requirement include a restriction on using JSON.parse(JSON.stringify(input)) to avoid mutating the original array?

    – guest271314
    7 hours ago













  • Wow, lots of great answers, thanks. I'm a little torn on which one to accept as a solution, so I have to try them out. I'll choose a solution in a day or so. but I voted you all up. Thanks again.

    – jimboweb
    4 hours ago



















  • "Is there a better way?" What do you mean by "better"? "I don't like it" is not an objective coding problem. What is the requirement?

    – guest271314
    8 hours ago













  • Well for one thing I'd like to do it without mutating any variables, or introducing a .forEach loop which isn't really functional. And I think it can be done much more concisely as well. If I could zip a set of arrays together like in Python then reduce(concat) them together that way would work too.

    – jimboweb
    8 hours ago











  • I think I pretty clearly said I'd like the result to not mutate any arrays or variables, as I am doing with the accumulator.splice. That is an objective requirement.

    – jimboweb
    8 hours ago











  • Does the requirement include a restriction on using JSON.parse(JSON.stringify(input)) to avoid mutating the original array?

    – guest271314
    7 hours ago













  • Wow, lots of great answers, thanks. I'm a little torn on which one to accept as a solution, so I have to try them out. I'll choose a solution in a day or so. but I voted you all up. Thanks again.

    – jimboweb
    4 hours ago

















"Is there a better way?" What do you mean by "better"? "I don't like it" is not an objective coding problem. What is the requirement?

– guest271314
8 hours ago







"Is there a better way?" What do you mean by "better"? "I don't like it" is not an objective coding problem. What is the requirement?

– guest271314
8 hours ago















Well for one thing I'd like to do it without mutating any variables, or introducing a .forEach loop which isn't really functional. And I think it can be done much more concisely as well. If I could zip a set of arrays together like in Python then reduce(concat) them together that way would work too.

– jimboweb
8 hours ago





Well for one thing I'd like to do it without mutating any variables, or introducing a .forEach loop which isn't really functional. And I think it can be done much more concisely as well. If I could zip a set of arrays together like in Python then reduce(concat) them together that way would work too.

– jimboweb
8 hours ago













I think I pretty clearly said I'd like the result to not mutate any arrays or variables, as I am doing with the accumulator.splice. That is an objective requirement.

– jimboweb
8 hours ago





I think I pretty clearly said I'd like the result to not mutate any arrays or variables, as I am doing with the accumulator.splice. That is an objective requirement.

– jimboweb
8 hours ago













Does the requirement include a restriction on using JSON.parse(JSON.stringify(input)) to avoid mutating the original array?

– guest271314
7 hours ago







Does the requirement include a restriction on using JSON.parse(JSON.stringify(input)) to avoid mutating the original array?

– guest271314
7 hours ago















Wow, lots of great answers, thanks. I'm a little torn on which one to accept as a solution, so I have to try them out. I'll choose a solution in a day or so. but I voted you all up. Thanks again.

– jimboweb
4 hours ago





Wow, lots of great answers, thanks. I'm a little torn on which one to accept as a solution, so I have to try them out. I'll choose a solution in a day or so. but I voted you all up. Thanks again.

– jimboweb
4 hours ago












7 Answers
7






active

oldest

votes


















5














Maybe just a simple for... i loop that checks each array for an item in position i






var input = [["one","two","three"],["uno","dos"],["1","2","3","4"],["1st","2nd","3rd"]]

var output =
var maxLen = Math.max(...input.map(arr => arr.length));

for (i=0; i < maxLen; i++) {
input.forEach(arr => { if (arr[i] !== undefined) output.push(arr[i]) })
}

console.log(output)





Simple, but predictable and readable





Avoiding For Each Loop



If you need to avoid forEach, here's a similar approach where you could: get the max child array length, build a range of integers that would've been created by the for loop ([1,2,3,4]), map each value to pivot the arrays, flatten the multi-dimensional array, and then filter out the empty cells.



First in discrete steps, and then as a one liner:



var input = [["one","two","three"],["uno","dos"],["1","2","3","4"],["1st","2nd","3rd"]];


Multiple Steps:



var maxLen = Math.max(...input.map(arr => arr.length));
var indexes = Array(maxLen).fill().map((_,i) => i);
var pivoted = indexes.map(i => input.map(arr => arr[i] ));
var flattened = pivoted.flat().filter(el => el !== undefined);


One Liner:



var output = Array(Math.max(...input.map(arr => arr.length))).fill().map((_,i) => i)
.map(i => input.map(arr => arr[i] ))
.flat().filter(el => el !== undefined)





share|improve this answer

































    4














    Use Array.from() to create a new array with the length of the longest sub array. To get the length of the longest sub array, get an array of the lengths with Array.map() and take the max item.



    Then collect the non undefined items at the current index from each sub array with Array.reduceRight() or Array.reduce() (depending on the order you want), and use Array.flat() to get a single array.






    const input = [["one","two","three"],["uno","dos"],["1","2","3","4"],["first","second","third"]]

    const result = Array.from(
    { length: Math.max(...input.map(o => o.length)) },
    (_, i) => input.reduceRight((r, o) =>
    o[i] === undefined ? r : [...r, o[i]]
    , )
    )
    .flat();

    console.log(result);








    share|improve this answer


























    • Cool! I like it!

      – KyleMit
      7 hours ago











    • -4 bytes [...Array(Math.max(...input.map(o => o.length)))].map((_,i)=>{})

      – guest271314
      6 hours ago











    • @guest271314 - you'll need to fill the array afterwards, because you can't map sparse items.

      – Ori Drori
      1 hour ago



















    3














    Funny solution




    1. add index as prefix on inner array

    2. Flatten the array

    3. sort the array

    4. Remove the prefix





    let input = [["one","two","three"],["uno","dos"],["1","2","3","4"],["first","second","third"]]
    let ranked=input.map(i=>i.map((j,k)=>k+'---'+j)).slice()
    console.log(ranked.flat().sort().map(i=>i.split('---')[1]));








    share|improve this answer


























    • Clever answer. I did consider doing something like this. I was thinking of something more like making the inner map return something like {ind:k,val:j} followed by a .sort((a,b)=>return a.ind-b.ind) but it's the same idea. I decided against it because creating unnecessary objects (or strings) felt like doing unnecessary work. But still a good answer, thanks.

      – jimboweb
      4 hours ago






    • 1





      yes this is bit messy , i admit it

      – sumit
      4 hours ago



















    2














    Here I have provided a generator function that will yield the values in the desired order. You could easily turn this into a regular function returning an array if you replace the yield with a push to a results array to be returned.



    The algorithm takes in all the arrays as arguments, then gets the iterators for each of them. Then it enters the main loop where it treats the iters array like a queue, taking the iterator in front, yielding the next generated value, then placing it back at the end of the queue unless it is empty. The efficiency would improve if you transformed the array into a linked list where adding to the front and back take constant time, whereas a shift on an array is linear time to shift everything down one spot.



    function* collate(...arrays) {
    const iters = arrays.map(a => a.values());
    while(iters.length > 0) {
    const iter = iters.shift();
    const {done, value} = iter.next();
    if(done) continue;
    yield value;
    iters.push(iter);
    }
    }





    share|improve this answer



















    • 1





      This is a great answer, thanks. But the while loop felt kind of like the .forEach I was trying to get around. But it's still a good answer so I voted it up.

      – jimboweb
      4 hours ago



















    1














    I made it with recursion approach to avoid mutation.






    let input = [["one","two","three"],["uno","dos"],["1","2","3","4"],["first","second","third"]]

    function recursion(input, idx = 0) {
    let tmp = input.map(elm => elm[idx])
    .filter(e => e !== undefined)
    return tmp[0] ? tmp.concat(recursion(input, idx + 1)) :
    }

    console.log(recursion(input))








    share|improve this answer


























    • Oh I like that! I didn't even think of using recursion. That's very elegant.

      – jimboweb
      2 hours ago



















    1














    Here's a recursive solution that meets the standards of elegance that you specified:






    const head = xs => xs[0];

    const tail = xs => xs.slice(1);

    const notNull = xs => xs.length > 0;

    console.log(collate([ ["one", "two", "three"]
    , ["uno", "dos"]
    , ["1", "2", "3", "4"]
    , ["first", "second", "third"]
    ]));

    function collate(xss) {
    if (xss.length === 0) return ;

    const yss = xss.filter(notNull);

    return yss.map(head).concat(collate(yss.map(tail)));
    }





    It can be directly translated into Haskell code:



    collate :: [[a]] -> [a]
    collate =
    collate xss = let yss = filter (not . null) xss
    in map head yss ++ collate (map tail yss)




    The previous solution took big steps to compute the answer. Here's a recursive solution that takes small steps to compute the answer:






    console.log(collate([ ["one", "two", "three"]
    , ["uno", "dos"]
    , ["1", "2", "3", "4"]
    , ["first", "second", "third"]
    ]));

    function collate(xss_) {
    if (xss_.length === 0) return ;

    const [xs_, ...xss] = xss_;

    if (xs_.length === 0) return collate(xss);

    const [x, ...xs] = xs_;

    return [x, ...collate(xss.concat([xs]))];
    }





    Here's the equivalent Haskell code:



    collate :: [[a]] -> [a]
    collate =
    collate (:xss) = collate xss
    collate ((x:xs):xss) = x : collate (xss ++ [xs])


    Hope that helps.






    share|improve this answer

































      0














      I've seen the problem called round-robin. I think a collate has a beautiful recursive definition -



      const collate = ([ v, ...vs ], acc = ) =>
      v === undefined
      ? acc
      : isEmpty (v)
      ? collate (vs, acc)
      : collate
      ( [ ...vs, tail (v) ]
      , [ ...acc, head (v) ]
      )


      The dependencies are isEmpty, tail, and head -



      const isEmpty = xs =>
      xs.length === 0

      const head = ([ x, ...xs ]) =>
      x

      const tail = ([ x, ...xs ]) =>
      xs


      It works like this -



      const input =
      [ [ "one", "two", "three" ]
      , [ "uno", "dos" ]
      , [ "1", "2", "3", "4" ]
      , [ "first", "second", "third" ]
      ]

      console.log (collate (input))
      // [ "one"
      // , "uno"
      // , "1"
      // , "first"
      // , "two"
      // , "dos"
      // , "2"
      // , "second"
      // , "three"
      // , "3"
      // , "third"
      // , "4"
      // ]


      Verify the results in your own browser below -






      const isEmpty = xs =>
      xs.length === 0

      const head = ([ x , ...xs ]) =>
      x

      const tail = ([ x , ...xs ]) =>
      xs

      const collate = ([ v, ...vs ], acc = ) =>
      v === undefined
      ? acc
      : isEmpty (v)
      ? collate (vs, acc)
      : collate
      ( [ ...vs, tail (v) ]
      , [ ...acc, head (v) ]
      )

      const input =
      [ [ "one", "two", "three" ]
      , [ "uno", "dos" ]
      , [ "1", "2", "3", "4" ]
      , [ "first", "second", "third" ]
      ]

      console.log (collate (input))
      // [ "one"
      // , "uno"
      // , "1"
      // , "first"
      // , "two"
      // , "dos"
      // , "2"
      // , "second"
      // , "three"
      // , "3"
      // , "third"
      // , "4"
      // ]








      share|improve this answer


























      • The v === undefined pattern is a terrible way to test whether an array is empty. What if the input was a sparse array such as [,2,3]? The only reliable way to test whether an array is empty is to check its length property.

        – Aadit M Shah
        33 mins ago













      Your Answer






      StackExchange.ifUsing("editor", function () {
      StackExchange.using("externalEditor", function () {
      StackExchange.using("snippets", function () {
      StackExchange.snippets.init();
      });
      });
      }, "code-snippets");

      StackExchange.ready(function() {
      var channelOptions = {
      tags: "".split(" "),
      id: "1"
      };
      initTagRenderer("".split(" "), "".split(" "), channelOptions);

      StackExchange.using("externalEditor", function() {
      // Have to fire editor after snippets, if snippets enabled
      if (StackExchange.settings.snippets.snippetsEnabled) {
      StackExchange.using("snippets", function() {
      createEditor();
      });
      }
      else {
      createEditor();
      }
      });

      function createEditor() {
      StackExchange.prepareEditor({
      heartbeatType: 'answer',
      autoActivateHeartbeat: false,
      convertImagesToLinks: true,
      noModals: true,
      showLowRepImageUploadWarning: true,
      reputationToPostImages: 10,
      bindNavPrevention: true,
      postfix: "",
      imageUploader: {
      brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
      contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
      allowUrls: true
      },
      onDemand: true,
      discardSelector: ".discard-answer"
      ,immediatelyShowMarkdownHelp:true
      });


      }
      });














      draft saved

      draft discarded


















      StackExchange.ready(
      function () {
      StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54796607%2freduce-array-of-arrays-into-on-array-in-collated-order%23new-answer', 'question_page');
      }
      );

      Post as a guest















      Required, but never shown

























      7 Answers
      7






      active

      oldest

      votes








      7 Answers
      7






      active

      oldest

      votes









      active

      oldest

      votes






      active

      oldest

      votes









      5














      Maybe just a simple for... i loop that checks each array for an item in position i






      var input = [["one","two","three"],["uno","dos"],["1","2","3","4"],["1st","2nd","3rd"]]

      var output =
      var maxLen = Math.max(...input.map(arr => arr.length));

      for (i=0; i < maxLen; i++) {
      input.forEach(arr => { if (arr[i] !== undefined) output.push(arr[i]) })
      }

      console.log(output)





      Simple, but predictable and readable





      Avoiding For Each Loop



      If you need to avoid forEach, here's a similar approach where you could: get the max child array length, build a range of integers that would've been created by the for loop ([1,2,3,4]), map each value to pivot the arrays, flatten the multi-dimensional array, and then filter out the empty cells.



      First in discrete steps, and then as a one liner:



      var input = [["one","two","three"],["uno","dos"],["1","2","3","4"],["1st","2nd","3rd"]];


      Multiple Steps:



      var maxLen = Math.max(...input.map(arr => arr.length));
      var indexes = Array(maxLen).fill().map((_,i) => i);
      var pivoted = indexes.map(i => input.map(arr => arr[i] ));
      var flattened = pivoted.flat().filter(el => el !== undefined);


      One Liner:



      var output = Array(Math.max(...input.map(arr => arr.length))).fill().map((_,i) => i)
      .map(i => input.map(arr => arr[i] ))
      .flat().filter(el => el !== undefined)





      share|improve this answer






























        5














        Maybe just a simple for... i loop that checks each array for an item in position i






        var input = [["one","two","three"],["uno","dos"],["1","2","3","4"],["1st","2nd","3rd"]]

        var output =
        var maxLen = Math.max(...input.map(arr => arr.length));

        for (i=0; i < maxLen; i++) {
        input.forEach(arr => { if (arr[i] !== undefined) output.push(arr[i]) })
        }

        console.log(output)





        Simple, but predictable and readable





        Avoiding For Each Loop



        If you need to avoid forEach, here's a similar approach where you could: get the max child array length, build a range of integers that would've been created by the for loop ([1,2,3,4]), map each value to pivot the arrays, flatten the multi-dimensional array, and then filter out the empty cells.



        First in discrete steps, and then as a one liner:



        var input = [["one","two","three"],["uno","dos"],["1","2","3","4"],["1st","2nd","3rd"]];


        Multiple Steps:



        var maxLen = Math.max(...input.map(arr => arr.length));
        var indexes = Array(maxLen).fill().map((_,i) => i);
        var pivoted = indexes.map(i => input.map(arr => arr[i] ));
        var flattened = pivoted.flat().filter(el => el !== undefined);


        One Liner:



        var output = Array(Math.max(...input.map(arr => arr.length))).fill().map((_,i) => i)
        .map(i => input.map(arr => arr[i] ))
        .flat().filter(el => el !== undefined)





        share|improve this answer




























          5












          5








          5







          Maybe just a simple for... i loop that checks each array for an item in position i






          var input = [["one","two","three"],["uno","dos"],["1","2","3","4"],["1st","2nd","3rd"]]

          var output =
          var maxLen = Math.max(...input.map(arr => arr.length));

          for (i=0; i < maxLen; i++) {
          input.forEach(arr => { if (arr[i] !== undefined) output.push(arr[i]) })
          }

          console.log(output)





          Simple, but predictable and readable





          Avoiding For Each Loop



          If you need to avoid forEach, here's a similar approach where you could: get the max child array length, build a range of integers that would've been created by the for loop ([1,2,3,4]), map each value to pivot the arrays, flatten the multi-dimensional array, and then filter out the empty cells.



          First in discrete steps, and then as a one liner:



          var input = [["one","two","three"],["uno","dos"],["1","2","3","4"],["1st","2nd","3rd"]];


          Multiple Steps:



          var maxLen = Math.max(...input.map(arr => arr.length));
          var indexes = Array(maxLen).fill().map((_,i) => i);
          var pivoted = indexes.map(i => input.map(arr => arr[i] ));
          var flattened = pivoted.flat().filter(el => el !== undefined);


          One Liner:



          var output = Array(Math.max(...input.map(arr => arr.length))).fill().map((_,i) => i)
          .map(i => input.map(arr => arr[i] ))
          .flat().filter(el => el !== undefined)





          share|improve this answer















          Maybe just a simple for... i loop that checks each array for an item in position i






          var input = [["one","two","three"],["uno","dos"],["1","2","3","4"],["1st","2nd","3rd"]]

          var output =
          var maxLen = Math.max(...input.map(arr => arr.length));

          for (i=0; i < maxLen; i++) {
          input.forEach(arr => { if (arr[i] !== undefined) output.push(arr[i]) })
          }

          console.log(output)





          Simple, but predictable and readable





          Avoiding For Each Loop



          If you need to avoid forEach, here's a similar approach where you could: get the max child array length, build a range of integers that would've been created by the for loop ([1,2,3,4]), map each value to pivot the arrays, flatten the multi-dimensional array, and then filter out the empty cells.



          First in discrete steps, and then as a one liner:



          var input = [["one","two","three"],["uno","dos"],["1","2","3","4"],["1st","2nd","3rd"]];


          Multiple Steps:



          var maxLen = Math.max(...input.map(arr => arr.length));
          var indexes = Array(maxLen).fill().map((_,i) => i);
          var pivoted = indexes.map(i => input.map(arr => arr[i] ));
          var flattened = pivoted.flat().filter(el => el !== undefined);


          One Liner:



          var output = Array(Math.max(...input.map(arr => arr.length))).fill().map((_,i) => i)
          .map(i => input.map(arr => arr[i] ))
          .flat().filter(el => el !== undefined)





          var input = [["one","two","three"],["uno","dos"],["1","2","3","4"],["1st","2nd","3rd"]]

          var output =
          var maxLen = Math.max(...input.map(arr => arr.length));

          for (i=0; i < maxLen; i++) {
          input.forEach(arr => { if (arr[i] !== undefined) output.push(arr[i]) })
          }

          console.log(output)





          var input = [["one","two","three"],["uno","dos"],["1","2","3","4"],["1st","2nd","3rd"]]

          var output =
          var maxLen = Math.max(...input.map(arr => arr.length));

          for (i=0; i < maxLen; i++) {
          input.forEach(arr => { if (arr[i] !== undefined) output.push(arr[i]) })
          }

          console.log(output)






          share|improve this answer














          share|improve this answer



          share|improve this answer








          edited 4 hours ago

























          answered 8 hours ago









          KyleMitKyleMit

          58.6k35241401




          58.6k35241401

























              4














              Use Array.from() to create a new array with the length of the longest sub array. To get the length of the longest sub array, get an array of the lengths with Array.map() and take the max item.



              Then collect the non undefined items at the current index from each sub array with Array.reduceRight() or Array.reduce() (depending on the order you want), and use Array.flat() to get a single array.






              const input = [["one","two","three"],["uno","dos"],["1","2","3","4"],["first","second","third"]]

              const result = Array.from(
              { length: Math.max(...input.map(o => o.length)) },
              (_, i) => input.reduceRight((r, o) =>
              o[i] === undefined ? r : [...r, o[i]]
              , )
              )
              .flat();

              console.log(result);








              share|improve this answer


























              • Cool! I like it!

                – KyleMit
                7 hours ago











              • -4 bytes [...Array(Math.max(...input.map(o => o.length)))].map((_,i)=>{})

                – guest271314
                6 hours ago











              • @guest271314 - you'll need to fill the array afterwards, because you can't map sparse items.

                – Ori Drori
                1 hour ago
















              4














              Use Array.from() to create a new array with the length of the longest sub array. To get the length of the longest sub array, get an array of the lengths with Array.map() and take the max item.



              Then collect the non undefined items at the current index from each sub array with Array.reduceRight() or Array.reduce() (depending on the order you want), and use Array.flat() to get a single array.






              const input = [["one","two","three"],["uno","dos"],["1","2","3","4"],["first","second","third"]]

              const result = Array.from(
              { length: Math.max(...input.map(o => o.length)) },
              (_, i) => input.reduceRight((r, o) =>
              o[i] === undefined ? r : [...r, o[i]]
              , )
              )
              .flat();

              console.log(result);








              share|improve this answer


























              • Cool! I like it!

                – KyleMit
                7 hours ago











              • -4 bytes [...Array(Math.max(...input.map(o => o.length)))].map((_,i)=>{})

                – guest271314
                6 hours ago











              • @guest271314 - you'll need to fill the array afterwards, because you can't map sparse items.

                – Ori Drori
                1 hour ago














              4












              4








              4







              Use Array.from() to create a new array with the length of the longest sub array. To get the length of the longest sub array, get an array of the lengths with Array.map() and take the max item.



              Then collect the non undefined items at the current index from each sub array with Array.reduceRight() or Array.reduce() (depending on the order you want), and use Array.flat() to get a single array.






              const input = [["one","two","three"],["uno","dos"],["1","2","3","4"],["first","second","third"]]

              const result = Array.from(
              { length: Math.max(...input.map(o => o.length)) },
              (_, i) => input.reduceRight((r, o) =>
              o[i] === undefined ? r : [...r, o[i]]
              , )
              )
              .flat();

              console.log(result);








              share|improve this answer















              Use Array.from() to create a new array with the length of the longest sub array. To get the length of the longest sub array, get an array of the lengths with Array.map() and take the max item.



              Then collect the non undefined items at the current index from each sub array with Array.reduceRight() or Array.reduce() (depending on the order you want), and use Array.flat() to get a single array.






              const input = [["one","two","three"],["uno","dos"],["1","2","3","4"],["first","second","third"]]

              const result = Array.from(
              { length: Math.max(...input.map(o => o.length)) },
              (_, i) => input.reduceRight((r, o) =>
              o[i] === undefined ? r : [...r, o[i]]
              , )
              )
              .flat();

              console.log(result);








              const input = [["one","two","three"],["uno","dos"],["1","2","3","4"],["first","second","third"]]

              const result = Array.from(
              { length: Math.max(...input.map(o => o.length)) },
              (_, i) => input.reduceRight((r, o) =>
              o[i] === undefined ? r : [...r, o[i]]
              , )
              )
              .flat();

              console.log(result);





              const input = [["one","two","three"],["uno","dos"],["1","2","3","4"],["first","second","third"]]

              const result = Array.from(
              { length: Math.max(...input.map(o => o.length)) },
              (_, i) => input.reduceRight((r, o) =>
              o[i] === undefined ? r : [...r, o[i]]
              , )
              )
              .flat();

              console.log(result);






              share|improve this answer














              share|improve this answer



              share|improve this answer








              edited 1 hour ago

























              answered 8 hours ago









              Ori DroriOri Drori

              77.9k138492




              77.9k138492













              • Cool! I like it!

                – KyleMit
                7 hours ago











              • -4 bytes [...Array(Math.max(...input.map(o => o.length)))].map((_,i)=>{})

                – guest271314
                6 hours ago











              • @guest271314 - you'll need to fill the array afterwards, because you can't map sparse items.

                – Ori Drori
                1 hour ago



















              • Cool! I like it!

                – KyleMit
                7 hours ago











              • -4 bytes [...Array(Math.max(...input.map(o => o.length)))].map((_,i)=>{})

                – guest271314
                6 hours ago











              • @guest271314 - you'll need to fill the array afterwards, because you can't map sparse items.

                – Ori Drori
                1 hour ago

















              Cool! I like it!

              – KyleMit
              7 hours ago





              Cool! I like it!

              – KyleMit
              7 hours ago













              -4 bytes [...Array(Math.max(...input.map(o => o.length)))].map((_,i)=>{})

              – guest271314
              6 hours ago





              -4 bytes [...Array(Math.max(...input.map(o => o.length)))].map((_,i)=>{})

              – guest271314
              6 hours ago













              @guest271314 - you'll need to fill the array afterwards, because you can't map sparse items.

              – Ori Drori
              1 hour ago





              @guest271314 - you'll need to fill the array afterwards, because you can't map sparse items.

              – Ori Drori
              1 hour ago











              3














              Funny solution




              1. add index as prefix on inner array

              2. Flatten the array

              3. sort the array

              4. Remove the prefix





              let input = [["one","two","three"],["uno","dos"],["1","2","3","4"],["first","second","third"]]
              let ranked=input.map(i=>i.map((j,k)=>k+'---'+j)).slice()
              console.log(ranked.flat().sort().map(i=>i.split('---')[1]));








              share|improve this answer


























              • Clever answer. I did consider doing something like this. I was thinking of something more like making the inner map return something like {ind:k,val:j} followed by a .sort((a,b)=>return a.ind-b.ind) but it's the same idea. I decided against it because creating unnecessary objects (or strings) felt like doing unnecessary work. But still a good answer, thanks.

                – jimboweb
                4 hours ago






              • 1





                yes this is bit messy , i admit it

                – sumit
                4 hours ago
















              3














              Funny solution




              1. add index as prefix on inner array

              2. Flatten the array

              3. sort the array

              4. Remove the prefix





              let input = [["one","two","three"],["uno","dos"],["1","2","3","4"],["first","second","third"]]
              let ranked=input.map(i=>i.map((j,k)=>k+'---'+j)).slice()
              console.log(ranked.flat().sort().map(i=>i.split('---')[1]));








              share|improve this answer


























              • Clever answer. I did consider doing something like this. I was thinking of something more like making the inner map return something like {ind:k,val:j} followed by a .sort((a,b)=>return a.ind-b.ind) but it's the same idea. I decided against it because creating unnecessary objects (or strings) felt like doing unnecessary work. But still a good answer, thanks.

                – jimboweb
                4 hours ago






              • 1





                yes this is bit messy , i admit it

                – sumit
                4 hours ago














              3












              3








              3







              Funny solution




              1. add index as prefix on inner array

              2. Flatten the array

              3. sort the array

              4. Remove the prefix





              let input = [["one","two","three"],["uno","dos"],["1","2","3","4"],["first","second","third"]]
              let ranked=input.map(i=>i.map((j,k)=>k+'---'+j)).slice()
              console.log(ranked.flat().sort().map(i=>i.split('---')[1]));








              share|improve this answer















              Funny solution




              1. add index as prefix on inner array

              2. Flatten the array

              3. sort the array

              4. Remove the prefix





              let input = [["one","two","three"],["uno","dos"],["1","2","3","4"],["first","second","third"]]
              let ranked=input.map(i=>i.map((j,k)=>k+'---'+j)).slice()
              console.log(ranked.flat().sort().map(i=>i.split('---')[1]));








              let input = [["one","two","three"],["uno","dos"],["1","2","3","4"],["first","second","third"]]
              let ranked=input.map(i=>i.map((j,k)=>k+'---'+j)).slice()
              console.log(ranked.flat().sort().map(i=>i.split('---')[1]));





              let input = [["one","two","three"],["uno","dos"],["1","2","3","4"],["first","second","third"]]
              let ranked=input.map(i=>i.map((j,k)=>k+'---'+j)).slice()
              console.log(ranked.flat().sort().map(i=>i.split('---')[1]));






              share|improve this answer














              share|improve this answer



              share|improve this answer








              edited 6 hours ago

























              answered 7 hours ago









              sumitsumit

              8,20893479




              8,20893479













              • Clever answer. I did consider doing something like this. I was thinking of something more like making the inner map return something like {ind:k,val:j} followed by a .sort((a,b)=>return a.ind-b.ind) but it's the same idea. I decided against it because creating unnecessary objects (or strings) felt like doing unnecessary work. But still a good answer, thanks.

                – jimboweb
                4 hours ago






              • 1





                yes this is bit messy , i admit it

                – sumit
                4 hours ago



















              • Clever answer. I did consider doing something like this. I was thinking of something more like making the inner map return something like {ind:k,val:j} followed by a .sort((a,b)=>return a.ind-b.ind) but it's the same idea. I decided against it because creating unnecessary objects (or strings) felt like doing unnecessary work. But still a good answer, thanks.

                – jimboweb
                4 hours ago






              • 1





                yes this is bit messy , i admit it

                – sumit
                4 hours ago

















              Clever answer. I did consider doing something like this. I was thinking of something more like making the inner map return something like {ind:k,val:j} followed by a .sort((a,b)=>return a.ind-b.ind) but it's the same idea. I decided against it because creating unnecessary objects (or strings) felt like doing unnecessary work. But still a good answer, thanks.

              – jimboweb
              4 hours ago





              Clever answer. I did consider doing something like this. I was thinking of something more like making the inner map return something like {ind:k,val:j} followed by a .sort((a,b)=>return a.ind-b.ind) but it's the same idea. I decided against it because creating unnecessary objects (or strings) felt like doing unnecessary work. But still a good answer, thanks.

              – jimboweb
              4 hours ago




              1




              1





              yes this is bit messy , i admit it

              – sumit
              4 hours ago





              yes this is bit messy , i admit it

              – sumit
              4 hours ago











              2














              Here I have provided a generator function that will yield the values in the desired order. You could easily turn this into a regular function returning an array if you replace the yield with a push to a results array to be returned.



              The algorithm takes in all the arrays as arguments, then gets the iterators for each of them. Then it enters the main loop where it treats the iters array like a queue, taking the iterator in front, yielding the next generated value, then placing it back at the end of the queue unless it is empty. The efficiency would improve if you transformed the array into a linked list where adding to the front and back take constant time, whereas a shift on an array is linear time to shift everything down one spot.



              function* collate(...arrays) {
              const iters = arrays.map(a => a.values());
              while(iters.length > 0) {
              const iter = iters.shift();
              const {done, value} = iter.next();
              if(done) continue;
              yield value;
              iters.push(iter);
              }
              }





              share|improve this answer



















              • 1





                This is a great answer, thanks. But the while loop felt kind of like the .forEach I was trying to get around. But it's still a good answer so I voted it up.

                – jimboweb
                4 hours ago
















              2














              Here I have provided a generator function that will yield the values in the desired order. You could easily turn this into a regular function returning an array if you replace the yield with a push to a results array to be returned.



              The algorithm takes in all the arrays as arguments, then gets the iterators for each of them. Then it enters the main loop where it treats the iters array like a queue, taking the iterator in front, yielding the next generated value, then placing it back at the end of the queue unless it is empty. The efficiency would improve if you transformed the array into a linked list where adding to the front and back take constant time, whereas a shift on an array is linear time to shift everything down one spot.



              function* collate(...arrays) {
              const iters = arrays.map(a => a.values());
              while(iters.length > 0) {
              const iter = iters.shift();
              const {done, value} = iter.next();
              if(done) continue;
              yield value;
              iters.push(iter);
              }
              }





              share|improve this answer



















              • 1





                This is a great answer, thanks. But the while loop felt kind of like the .forEach I was trying to get around. But it's still a good answer so I voted it up.

                – jimboweb
                4 hours ago














              2












              2








              2







              Here I have provided a generator function that will yield the values in the desired order. You could easily turn this into a regular function returning an array if you replace the yield with a push to a results array to be returned.



              The algorithm takes in all the arrays as arguments, then gets the iterators for each of them. Then it enters the main loop where it treats the iters array like a queue, taking the iterator in front, yielding the next generated value, then placing it back at the end of the queue unless it is empty. The efficiency would improve if you transformed the array into a linked list where adding to the front and back take constant time, whereas a shift on an array is linear time to shift everything down one spot.



              function* collate(...arrays) {
              const iters = arrays.map(a => a.values());
              while(iters.length > 0) {
              const iter = iters.shift();
              const {done, value} = iter.next();
              if(done) continue;
              yield value;
              iters.push(iter);
              }
              }





              share|improve this answer













              Here I have provided a generator function that will yield the values in the desired order. You could easily turn this into a regular function returning an array if you replace the yield with a push to a results array to be returned.



              The algorithm takes in all the arrays as arguments, then gets the iterators for each of them. Then it enters the main loop where it treats the iters array like a queue, taking the iterator in front, yielding the next generated value, then placing it back at the end of the queue unless it is empty. The efficiency would improve if you transformed the array into a linked list where adding to the front and back take constant time, whereas a shift on an array is linear time to shift everything down one spot.



              function* collate(...arrays) {
              const iters = arrays.map(a => a.values());
              while(iters.length > 0) {
              const iter = iters.shift();
              const {done, value} = iter.next();
              if(done) continue;
              yield value;
              iters.push(iter);
              }
              }






              share|improve this answer












              share|improve this answer



              share|improve this answer










              answered 6 hours ago









              kamoroso94kamoroso94

              1,38511316




              1,38511316








              • 1





                This is a great answer, thanks. But the while loop felt kind of like the .forEach I was trying to get around. But it's still a good answer so I voted it up.

                – jimboweb
                4 hours ago














              • 1





                This is a great answer, thanks. But the while loop felt kind of like the .forEach I was trying to get around. But it's still a good answer so I voted it up.

                – jimboweb
                4 hours ago








              1




              1





              This is a great answer, thanks. But the while loop felt kind of like the .forEach I was trying to get around. But it's still a good answer so I voted it up.

              – jimboweb
              4 hours ago





              This is a great answer, thanks. But the while loop felt kind of like the .forEach I was trying to get around. But it's still a good answer so I voted it up.

              – jimboweb
              4 hours ago











              1














              I made it with recursion approach to avoid mutation.






              let input = [["one","two","three"],["uno","dos"],["1","2","3","4"],["first","second","third"]]

              function recursion(input, idx = 0) {
              let tmp = input.map(elm => elm[idx])
              .filter(e => e !== undefined)
              return tmp[0] ? tmp.concat(recursion(input, idx + 1)) :
              }

              console.log(recursion(input))








              share|improve this answer


























              • Oh I like that! I didn't even think of using recursion. That's very elegant.

                – jimboweb
                2 hours ago
















              1














              I made it with recursion approach to avoid mutation.






              let input = [["one","two","three"],["uno","dos"],["1","2","3","4"],["first","second","third"]]

              function recursion(input, idx = 0) {
              let tmp = input.map(elm => elm[idx])
              .filter(e => e !== undefined)
              return tmp[0] ? tmp.concat(recursion(input, idx + 1)) :
              }

              console.log(recursion(input))








              share|improve this answer


























              • Oh I like that! I didn't even think of using recursion. That's very elegant.

                – jimboweb
                2 hours ago














              1












              1








              1







              I made it with recursion approach to avoid mutation.






              let input = [["one","two","three"],["uno","dos"],["1","2","3","4"],["first","second","third"]]

              function recursion(input, idx = 0) {
              let tmp = input.map(elm => elm[idx])
              .filter(e => e !== undefined)
              return tmp[0] ? tmp.concat(recursion(input, idx + 1)) :
              }

              console.log(recursion(input))








              share|improve this answer















              I made it with recursion approach to avoid mutation.






              let input = [["one","two","three"],["uno","dos"],["1","2","3","4"],["first","second","third"]]

              function recursion(input, idx = 0) {
              let tmp = input.map(elm => elm[idx])
              .filter(e => e !== undefined)
              return tmp[0] ? tmp.concat(recursion(input, idx + 1)) :
              }

              console.log(recursion(input))








              let input = [["one","two","three"],["uno","dos"],["1","2","3","4"],["first","second","third"]]

              function recursion(input, idx = 0) {
              let tmp = input.map(elm => elm[idx])
              .filter(e => e !== undefined)
              return tmp[0] ? tmp.concat(recursion(input, idx + 1)) :
              }

              console.log(recursion(input))





              let input = [["one","two","three"],["uno","dos"],["1","2","3","4"],["first","second","third"]]

              function recursion(input, idx = 0) {
              let tmp = input.map(elm => elm[idx])
              .filter(e => e !== undefined)
              return tmp[0] ? tmp.concat(recursion(input, idx + 1)) :
              }

              console.log(recursion(input))






              share|improve this answer














              share|improve this answer



              share|improve this answer








              edited 2 hours ago

























              answered 3 hours ago









              Tam DcTam Dc

              115412




              115412













              • Oh I like that! I didn't even think of using recursion. That's very elegant.

                – jimboweb
                2 hours ago



















              • Oh I like that! I didn't even think of using recursion. That's very elegant.

                – jimboweb
                2 hours ago

















              Oh I like that! I didn't even think of using recursion. That's very elegant.

              – jimboweb
              2 hours ago





              Oh I like that! I didn't even think of using recursion. That's very elegant.

              – jimboweb
              2 hours ago











              1














              Here's a recursive solution that meets the standards of elegance that you specified:






              const head = xs => xs[0];

              const tail = xs => xs.slice(1);

              const notNull = xs => xs.length > 0;

              console.log(collate([ ["one", "two", "three"]
              , ["uno", "dos"]
              , ["1", "2", "3", "4"]
              , ["first", "second", "third"]
              ]));

              function collate(xss) {
              if (xss.length === 0) return ;

              const yss = xss.filter(notNull);

              return yss.map(head).concat(collate(yss.map(tail)));
              }





              It can be directly translated into Haskell code:



              collate :: [[a]] -> [a]
              collate =
              collate xss = let yss = filter (not . null) xss
              in map head yss ++ collate (map tail yss)




              The previous solution took big steps to compute the answer. Here's a recursive solution that takes small steps to compute the answer:






              console.log(collate([ ["one", "two", "three"]
              , ["uno", "dos"]
              , ["1", "2", "3", "4"]
              , ["first", "second", "third"]
              ]));

              function collate(xss_) {
              if (xss_.length === 0) return ;

              const [xs_, ...xss] = xss_;

              if (xs_.length === 0) return collate(xss);

              const [x, ...xs] = xs_;

              return [x, ...collate(xss.concat([xs]))];
              }





              Here's the equivalent Haskell code:



              collate :: [[a]] -> [a]
              collate =
              collate (:xss) = collate xss
              collate ((x:xs):xss) = x : collate (xss ++ [xs])


              Hope that helps.






              share|improve this answer






























                1














                Here's a recursive solution that meets the standards of elegance that you specified:






                const head = xs => xs[0];

                const tail = xs => xs.slice(1);

                const notNull = xs => xs.length > 0;

                console.log(collate([ ["one", "two", "three"]
                , ["uno", "dos"]
                , ["1", "2", "3", "4"]
                , ["first", "second", "third"]
                ]));

                function collate(xss) {
                if (xss.length === 0) return ;

                const yss = xss.filter(notNull);

                return yss.map(head).concat(collate(yss.map(tail)));
                }





                It can be directly translated into Haskell code:



                collate :: [[a]] -> [a]
                collate =
                collate xss = let yss = filter (not . null) xss
                in map head yss ++ collate (map tail yss)




                The previous solution took big steps to compute the answer. Here's a recursive solution that takes small steps to compute the answer:






                console.log(collate([ ["one", "two", "three"]
                , ["uno", "dos"]
                , ["1", "2", "3", "4"]
                , ["first", "second", "third"]
                ]));

                function collate(xss_) {
                if (xss_.length === 0) return ;

                const [xs_, ...xss] = xss_;

                if (xs_.length === 0) return collate(xss);

                const [x, ...xs] = xs_;

                return [x, ...collate(xss.concat([xs]))];
                }





                Here's the equivalent Haskell code:



                collate :: [[a]] -> [a]
                collate =
                collate (:xss) = collate xss
                collate ((x:xs):xss) = x : collate (xss ++ [xs])


                Hope that helps.






                share|improve this answer




























                  1












                  1








                  1







                  Here's a recursive solution that meets the standards of elegance that you specified:






                  const head = xs => xs[0];

                  const tail = xs => xs.slice(1);

                  const notNull = xs => xs.length > 0;

                  console.log(collate([ ["one", "two", "three"]
                  , ["uno", "dos"]
                  , ["1", "2", "3", "4"]
                  , ["first", "second", "third"]
                  ]));

                  function collate(xss) {
                  if (xss.length === 0) return ;

                  const yss = xss.filter(notNull);

                  return yss.map(head).concat(collate(yss.map(tail)));
                  }





                  It can be directly translated into Haskell code:



                  collate :: [[a]] -> [a]
                  collate =
                  collate xss = let yss = filter (not . null) xss
                  in map head yss ++ collate (map tail yss)




                  The previous solution took big steps to compute the answer. Here's a recursive solution that takes small steps to compute the answer:






                  console.log(collate([ ["one", "two", "three"]
                  , ["uno", "dos"]
                  , ["1", "2", "3", "4"]
                  , ["first", "second", "third"]
                  ]));

                  function collate(xss_) {
                  if (xss_.length === 0) return ;

                  const [xs_, ...xss] = xss_;

                  if (xs_.length === 0) return collate(xss);

                  const [x, ...xs] = xs_;

                  return [x, ...collate(xss.concat([xs]))];
                  }





                  Here's the equivalent Haskell code:



                  collate :: [[a]] -> [a]
                  collate =
                  collate (:xss) = collate xss
                  collate ((x:xs):xss) = x : collate (xss ++ [xs])


                  Hope that helps.






                  share|improve this answer















                  Here's a recursive solution that meets the standards of elegance that you specified:






                  const head = xs => xs[0];

                  const tail = xs => xs.slice(1);

                  const notNull = xs => xs.length > 0;

                  console.log(collate([ ["one", "two", "three"]
                  , ["uno", "dos"]
                  , ["1", "2", "3", "4"]
                  , ["first", "second", "third"]
                  ]));

                  function collate(xss) {
                  if (xss.length === 0) return ;

                  const yss = xss.filter(notNull);

                  return yss.map(head).concat(collate(yss.map(tail)));
                  }





                  It can be directly translated into Haskell code:



                  collate :: [[a]] -> [a]
                  collate =
                  collate xss = let yss = filter (not . null) xss
                  in map head yss ++ collate (map tail yss)




                  The previous solution took big steps to compute the answer. Here's a recursive solution that takes small steps to compute the answer:






                  console.log(collate([ ["one", "two", "three"]
                  , ["uno", "dos"]
                  , ["1", "2", "3", "4"]
                  , ["first", "second", "third"]
                  ]));

                  function collate(xss_) {
                  if (xss_.length === 0) return ;

                  const [xs_, ...xss] = xss_;

                  if (xs_.length === 0) return collate(xss);

                  const [x, ...xs] = xs_;

                  return [x, ...collate(xss.concat([xs]))];
                  }





                  Here's the equivalent Haskell code:



                  collate :: [[a]] -> [a]
                  collate =
                  collate (:xss) = collate xss
                  collate ((x:xs):xss) = x : collate (xss ++ [xs])


                  Hope that helps.






                  const head = xs => xs[0];

                  const tail = xs => xs.slice(1);

                  const notNull = xs => xs.length > 0;

                  console.log(collate([ ["one", "two", "three"]
                  , ["uno", "dos"]
                  , ["1", "2", "3", "4"]
                  , ["first", "second", "third"]
                  ]));

                  function collate(xss) {
                  if (xss.length === 0) return ;

                  const yss = xss.filter(notNull);

                  return yss.map(head).concat(collate(yss.map(tail)));
                  }





                  const head = xs => xs[0];

                  const tail = xs => xs.slice(1);

                  const notNull = xs => xs.length > 0;

                  console.log(collate([ ["one", "two", "three"]
                  , ["uno", "dos"]
                  , ["1", "2", "3", "4"]
                  , ["first", "second", "third"]
                  ]));

                  function collate(xss) {
                  if (xss.length === 0) return ;

                  const yss = xss.filter(notNull);

                  return yss.map(head).concat(collate(yss.map(tail)));
                  }





                  console.log(collate([ ["one", "two", "three"]
                  , ["uno", "dos"]
                  , ["1", "2", "3", "4"]
                  , ["first", "second", "third"]
                  ]));

                  function collate(xss_) {
                  if (xss_.length === 0) return ;

                  const [xs_, ...xss] = xss_;

                  if (xs_.length === 0) return collate(xss);

                  const [x, ...xs] = xs_;

                  return [x, ...collate(xss.concat([xs]))];
                  }





                  console.log(collate([ ["one", "two", "three"]
                  , ["uno", "dos"]
                  , ["1", "2", "3", "4"]
                  , ["first", "second", "third"]
                  ]));

                  function collate(xss_) {
                  if (xss_.length === 0) return ;

                  const [xs_, ...xss] = xss_;

                  if (xs_.length === 0) return collate(xss);

                  const [x, ...xs] = xs_;

                  return [x, ...collate(xss.concat([xs]))];
                  }






                  share|improve this answer














                  share|improve this answer



                  share|improve this answer








                  edited 40 mins ago

























                  answered 2 hours ago









                  Aadit M ShahAadit M Shah

                  49.8k18111226




                  49.8k18111226























                      0














                      I've seen the problem called round-robin. I think a collate has a beautiful recursive definition -



                      const collate = ([ v, ...vs ], acc = ) =>
                      v === undefined
                      ? acc
                      : isEmpty (v)
                      ? collate (vs, acc)
                      : collate
                      ( [ ...vs, tail (v) ]
                      , [ ...acc, head (v) ]
                      )


                      The dependencies are isEmpty, tail, and head -



                      const isEmpty = xs =>
                      xs.length === 0

                      const head = ([ x, ...xs ]) =>
                      x

                      const tail = ([ x, ...xs ]) =>
                      xs


                      It works like this -



                      const input =
                      [ [ "one", "two", "three" ]
                      , [ "uno", "dos" ]
                      , [ "1", "2", "3", "4" ]
                      , [ "first", "second", "third" ]
                      ]

                      console.log (collate (input))
                      // [ "one"
                      // , "uno"
                      // , "1"
                      // , "first"
                      // , "two"
                      // , "dos"
                      // , "2"
                      // , "second"
                      // , "three"
                      // , "3"
                      // , "third"
                      // , "4"
                      // ]


                      Verify the results in your own browser below -






                      const isEmpty = xs =>
                      xs.length === 0

                      const head = ([ x , ...xs ]) =>
                      x

                      const tail = ([ x , ...xs ]) =>
                      xs

                      const collate = ([ v, ...vs ], acc = ) =>
                      v === undefined
                      ? acc
                      : isEmpty (v)
                      ? collate (vs, acc)
                      : collate
                      ( [ ...vs, tail (v) ]
                      , [ ...acc, head (v) ]
                      )

                      const input =
                      [ [ "one", "two", "three" ]
                      , [ "uno", "dos" ]
                      , [ "1", "2", "3", "4" ]
                      , [ "first", "second", "third" ]
                      ]

                      console.log (collate (input))
                      // [ "one"
                      // , "uno"
                      // , "1"
                      // , "first"
                      // , "two"
                      // , "dos"
                      // , "2"
                      // , "second"
                      // , "three"
                      // , "3"
                      // , "third"
                      // , "4"
                      // ]








                      share|improve this answer


























                      • The v === undefined pattern is a terrible way to test whether an array is empty. What if the input was a sparse array such as [,2,3]? The only reliable way to test whether an array is empty is to check its length property.

                        – Aadit M Shah
                        33 mins ago


















                      0














                      I've seen the problem called round-robin. I think a collate has a beautiful recursive definition -



                      const collate = ([ v, ...vs ], acc = ) =>
                      v === undefined
                      ? acc
                      : isEmpty (v)
                      ? collate (vs, acc)
                      : collate
                      ( [ ...vs, tail (v) ]
                      , [ ...acc, head (v) ]
                      )


                      The dependencies are isEmpty, tail, and head -



                      const isEmpty = xs =>
                      xs.length === 0

                      const head = ([ x, ...xs ]) =>
                      x

                      const tail = ([ x, ...xs ]) =>
                      xs


                      It works like this -



                      const input =
                      [ [ "one", "two", "three" ]
                      , [ "uno", "dos" ]
                      , [ "1", "2", "3", "4" ]
                      , [ "first", "second", "third" ]
                      ]

                      console.log (collate (input))
                      // [ "one"
                      // , "uno"
                      // , "1"
                      // , "first"
                      // , "two"
                      // , "dos"
                      // , "2"
                      // , "second"
                      // , "three"
                      // , "3"
                      // , "third"
                      // , "4"
                      // ]


                      Verify the results in your own browser below -






                      const isEmpty = xs =>
                      xs.length === 0

                      const head = ([ x , ...xs ]) =>
                      x

                      const tail = ([ x , ...xs ]) =>
                      xs

                      const collate = ([ v, ...vs ], acc = ) =>
                      v === undefined
                      ? acc
                      : isEmpty (v)
                      ? collate (vs, acc)
                      : collate
                      ( [ ...vs, tail (v) ]
                      , [ ...acc, head (v) ]
                      )

                      const input =
                      [ [ "one", "two", "three" ]
                      , [ "uno", "dos" ]
                      , [ "1", "2", "3", "4" ]
                      , [ "first", "second", "third" ]
                      ]

                      console.log (collate (input))
                      // [ "one"
                      // , "uno"
                      // , "1"
                      // , "first"
                      // , "two"
                      // , "dos"
                      // , "2"
                      // , "second"
                      // , "three"
                      // , "3"
                      // , "third"
                      // , "4"
                      // ]








                      share|improve this answer


























                      • The v === undefined pattern is a terrible way to test whether an array is empty. What if the input was a sparse array such as [,2,3]? The only reliable way to test whether an array is empty is to check its length property.

                        – Aadit M Shah
                        33 mins ago
















                      0












                      0








                      0







                      I've seen the problem called round-robin. I think a collate has a beautiful recursive definition -



                      const collate = ([ v, ...vs ], acc = ) =>
                      v === undefined
                      ? acc
                      : isEmpty (v)
                      ? collate (vs, acc)
                      : collate
                      ( [ ...vs, tail (v) ]
                      , [ ...acc, head (v) ]
                      )


                      The dependencies are isEmpty, tail, and head -



                      const isEmpty = xs =>
                      xs.length === 0

                      const head = ([ x, ...xs ]) =>
                      x

                      const tail = ([ x, ...xs ]) =>
                      xs


                      It works like this -



                      const input =
                      [ [ "one", "two", "three" ]
                      , [ "uno", "dos" ]
                      , [ "1", "2", "3", "4" ]
                      , [ "first", "second", "third" ]
                      ]

                      console.log (collate (input))
                      // [ "one"
                      // , "uno"
                      // , "1"
                      // , "first"
                      // , "two"
                      // , "dos"
                      // , "2"
                      // , "second"
                      // , "three"
                      // , "3"
                      // , "third"
                      // , "4"
                      // ]


                      Verify the results in your own browser below -






                      const isEmpty = xs =>
                      xs.length === 0

                      const head = ([ x , ...xs ]) =>
                      x

                      const tail = ([ x , ...xs ]) =>
                      xs

                      const collate = ([ v, ...vs ], acc = ) =>
                      v === undefined
                      ? acc
                      : isEmpty (v)
                      ? collate (vs, acc)
                      : collate
                      ( [ ...vs, tail (v) ]
                      , [ ...acc, head (v) ]
                      )

                      const input =
                      [ [ "one", "two", "three" ]
                      , [ "uno", "dos" ]
                      , [ "1", "2", "3", "4" ]
                      , [ "first", "second", "third" ]
                      ]

                      console.log (collate (input))
                      // [ "one"
                      // , "uno"
                      // , "1"
                      // , "first"
                      // , "two"
                      // , "dos"
                      // , "2"
                      // , "second"
                      // , "three"
                      // , "3"
                      // , "third"
                      // , "4"
                      // ]








                      share|improve this answer















                      I've seen the problem called round-robin. I think a collate has a beautiful recursive definition -



                      const collate = ([ v, ...vs ], acc = ) =>
                      v === undefined
                      ? acc
                      : isEmpty (v)
                      ? collate (vs, acc)
                      : collate
                      ( [ ...vs, tail (v) ]
                      , [ ...acc, head (v) ]
                      )


                      The dependencies are isEmpty, tail, and head -



                      const isEmpty = xs =>
                      xs.length === 0

                      const head = ([ x, ...xs ]) =>
                      x

                      const tail = ([ x, ...xs ]) =>
                      xs


                      It works like this -



                      const input =
                      [ [ "one", "two", "three" ]
                      , [ "uno", "dos" ]
                      , [ "1", "2", "3", "4" ]
                      , [ "first", "second", "third" ]
                      ]

                      console.log (collate (input))
                      // [ "one"
                      // , "uno"
                      // , "1"
                      // , "first"
                      // , "two"
                      // , "dos"
                      // , "2"
                      // , "second"
                      // , "three"
                      // , "3"
                      // , "third"
                      // , "4"
                      // ]


                      Verify the results in your own browser below -






                      const isEmpty = xs =>
                      xs.length === 0

                      const head = ([ x , ...xs ]) =>
                      x

                      const tail = ([ x , ...xs ]) =>
                      xs

                      const collate = ([ v, ...vs ], acc = ) =>
                      v === undefined
                      ? acc
                      : isEmpty (v)
                      ? collate (vs, acc)
                      : collate
                      ( [ ...vs, tail (v) ]
                      , [ ...acc, head (v) ]
                      )

                      const input =
                      [ [ "one", "two", "three" ]
                      , [ "uno", "dos" ]
                      , [ "1", "2", "3", "4" ]
                      , [ "first", "second", "third" ]
                      ]

                      console.log (collate (input))
                      // [ "one"
                      // , "uno"
                      // , "1"
                      // , "first"
                      // , "two"
                      // , "dos"
                      // , "2"
                      // , "second"
                      // , "three"
                      // , "3"
                      // , "third"
                      // , "4"
                      // ]








                      const isEmpty = xs =>
                      xs.length === 0

                      const head = ([ x , ...xs ]) =>
                      x

                      const tail = ([ x , ...xs ]) =>
                      xs

                      const collate = ([ v, ...vs ], acc = ) =>
                      v === undefined
                      ? acc
                      : isEmpty (v)
                      ? collate (vs, acc)
                      : collate
                      ( [ ...vs, tail (v) ]
                      , [ ...acc, head (v) ]
                      )

                      const input =
                      [ [ "one", "two", "three" ]
                      , [ "uno", "dos" ]
                      , [ "1", "2", "3", "4" ]
                      , [ "first", "second", "third" ]
                      ]

                      console.log (collate (input))
                      // [ "one"
                      // , "uno"
                      // , "1"
                      // , "first"
                      // , "two"
                      // , "dos"
                      // , "2"
                      // , "second"
                      // , "three"
                      // , "3"
                      // , "third"
                      // , "4"
                      // ]





                      const isEmpty = xs =>
                      xs.length === 0

                      const head = ([ x , ...xs ]) =>
                      x

                      const tail = ([ x , ...xs ]) =>
                      xs

                      const collate = ([ v, ...vs ], acc = ) =>
                      v === undefined
                      ? acc
                      : isEmpty (v)
                      ? collate (vs, acc)
                      : collate
                      ( [ ...vs, tail (v) ]
                      , [ ...acc, head (v) ]
                      )

                      const input =
                      [ [ "one", "two", "three" ]
                      , [ "uno", "dos" ]
                      , [ "1", "2", "3", "4" ]
                      , [ "first", "second", "third" ]
                      ]

                      console.log (collate (input))
                      // [ "one"
                      // , "uno"
                      // , "1"
                      // , "first"
                      // , "two"
                      // , "dos"
                      // , "2"
                      // , "second"
                      // , "three"
                      // , "3"
                      // , "third"
                      // , "4"
                      // ]






                      share|improve this answer














                      share|improve this answer



                      share|improve this answer








                      edited 53 mins ago

























                      answered 1 hour ago









                      user633183user633183

                      70.6k21139180




                      70.6k21139180













                      • The v === undefined pattern is a terrible way to test whether an array is empty. What if the input was a sparse array such as [,2,3]? The only reliable way to test whether an array is empty is to check its length property.

                        – Aadit M Shah
                        33 mins ago





















                      • The v === undefined pattern is a terrible way to test whether an array is empty. What if the input was a sparse array such as [,2,3]? The only reliable way to test whether an array is empty is to check its length property.

                        – Aadit M Shah
                        33 mins ago



















                      The v === undefined pattern is a terrible way to test whether an array is empty. What if the input was a sparse array such as [,2,3]? The only reliable way to test whether an array is empty is to check its length property.

                      – Aadit M Shah
                      33 mins ago







                      The v === undefined pattern is a terrible way to test whether an array is empty. What if the input was a sparse array such as [,2,3]? The only reliable way to test whether an array is empty is to check its length property.

                      – Aadit M Shah
                      33 mins ago




















                      draft saved

                      draft discarded




















































                      Thanks for contributing an answer to Stack Overflow!


                      • Please be sure to answer the question. Provide details and share your research!

                      But avoid



                      • Asking for help, clarification, or responding to other answers.

                      • Making statements based on opinion; back them up with references or personal experience.


                      To learn more, see our tips on writing great answers.




                      draft saved


                      draft discarded














                      StackExchange.ready(
                      function () {
                      StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54796607%2freduce-array-of-arrays-into-on-array-in-collated-order%23new-answer', 'question_page');
                      }
                      );

                      Post as a guest















                      Required, but never shown





















































                      Required, but never shown














                      Required, but never shown












                      Required, but never shown







                      Required, but never shown

































                      Required, but never shown














                      Required, but never shown












                      Required, but never shown







                      Required, but never shown







                      Popular posts from this blog

                      If I really need a card on my start hand, how many mulligans make sense? [duplicate]

                      Alcedinidae

                      Can an atomic nucleus contain both particles and antiparticles? [duplicate]