reduce array of arrays into on array in collated order












6















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
    6 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
    6 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
    6 hours ago











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

    – guest271314
    5 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
    2 hours ago
















6















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
    6 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
    6 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
    6 hours ago











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

    – guest271314
    5 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
    2 hours ago














6












6








6


1






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 6 hours ago







jimboweb

















asked 6 hours ago









jimbowebjimboweb

1,74111027




1,74111027













  • "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
    6 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
    6 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
    6 hours ago











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

    – guest271314
    5 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
    2 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
    6 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
    6 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
    6 hours ago











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

    – guest271314
    5 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
    2 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
6 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
6 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
6 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
6 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
6 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
6 hours ago













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

– guest271314
5 hours ago







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

– guest271314
5 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
2 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
2 hours ago












6 Answers
6






active

oldest

votes


















4














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

































    3














    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
      5 hours ago











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

      – guest271314
      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
      2 hours ago






    • 1





      yes this is bit messy , i admit it

      – sumit
      2 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
      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
      46 mins ago



















    0














    Here's a recursive solution. It's not the most efficient, but it meets the standards of elegance that you specified:






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

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

    const [ys, ...xss] = yss;

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

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

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





    It can be directly translated into 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























      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

























      6 Answers
      6






      active

      oldest

      votes








      6 Answers
      6






      active

      oldest

      votes









      active

      oldest

      votes






      active

      oldest

      votes









      4














      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














        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












          4








          4







          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 2 hours ago

























          answered 6 hours ago









          KyleMitKyleMit

          58.6k35241401




          58.6k35241401

























              3














              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
                5 hours ago











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

                – guest271314
                4 hours ago
















              3














              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
                5 hours ago











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

                – guest271314
                4 hours ago














              3












              3








              3







              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 6 hours ago

























              answered 6 hours ago









              Ori DroriOri Drori

              77.9k138492




              77.9k138492













              • Cool! I like it!

                – KyleMit
                5 hours ago











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

                – guest271314
                4 hours ago



















              • Cool! I like it!

                – KyleMit
                5 hours ago











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

                – guest271314
                4 hours ago

















              Cool! I like it!

              – KyleMit
              5 hours ago





              Cool! I like it!

              – KyleMit
              5 hours ago













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

              – guest271314
              4 hours ago





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

              – guest271314
              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
                2 hours ago






              • 1





                yes this is bit messy , i admit it

                – sumit
                2 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
                2 hours ago






              • 1





                yes this is bit messy , i admit it

                – sumit
                2 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 5 hours ago

























              answered 6 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
                2 hours ago






              • 1





                yes this is bit messy , i admit it

                – sumit
                2 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
                2 hours ago






              • 1





                yes this is bit messy , i admit it

                – sumit
                2 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
              2 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
              2 hours ago




              1




              1





              yes this is bit messy , i admit it

              – sumit
              2 hours ago





              yes this is bit messy , i admit it

              – sumit
              2 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
                2 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
                2 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 4 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
                2 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
                2 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
              2 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
              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
                46 mins 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
                46 mins 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 45 mins ago

























              answered 1 hour ago









              Tam DcTam Dc

              115412




              115412













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

                – jimboweb
                46 mins ago



















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

                – jimboweb
                46 mins ago

















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

              – jimboweb
              46 mins ago





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

              – jimboweb
              46 mins ago











              0














              Here's a recursive solution. It's not the most efficient, but it meets the standards of elegance that you specified:






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

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

              const [ys, ...xss] = yss;

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

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

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





              It can be directly translated into 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














                Here's a recursive solution. It's not the most efficient, but it meets the standards of elegance that you specified:






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

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

                const [ys, ...xss] = yss;

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

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

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





                It can be directly translated into 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












                  0








                  0







                  Here's a recursive solution. It's not the most efficient, but it meets the standards of elegance that you specified:






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

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

                  const [ys, ...xss] = yss;

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

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

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





                  It can be directly translated into 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. It's not the most efficient, but it meets the standards of elegance that you specified:






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

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

                  const [ys, ...xss] = yss;

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

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

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





                  It can be directly translated into Haskell code:



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


                  Hope that helps.






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

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

                  const [ys, ...xss] = yss;

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

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

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





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

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

                  const [ys, ...xss] = yss;

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

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

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






                  share|improve this answer












                  share|improve this answer



                  share|improve this answer










                  answered 24 mins ago









                  Aadit M ShahAadit M Shah

                  49.8k18111226




                  49.8k18111226






























                      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

                      Plaza Victoria

                      In PowerPoint, is there a keyboard shortcut for bulleted / numbered list?

                      How to put 3 figures in Latex with 2 figures side by side and 1 below these side by side images but in...