I've seen the problem called round-robin, but maybe interleave is a better name. Sure, map, reduce, and filter are functional procedures, but not all functional programs need rely on them. When these are the only functions we know how to use, the resulting program is sometimes awkward because there is often a better fit.
- mapproduces a one-to-one result. If we have 4 subarrays, our result will have 4 elements.- interleaveshould produce a result equal to the length of the combined subarrays, so- mapcould only possibly get us part way there. Additional steps would be required to get the final result.
 
- reduceiterates through the input elements one-at-a-time to produce a final result. In the first reduce, we will be given the first subarray, but there's no straightforward way to process the entire subarray before moving onto the next one. We can force our program to use- reduce, but in doing so, it makes us think about our collation procedure as a recuding procedure instead of what it actually is.
 
The reality is you're not limited to the use of these primitive functional procedures. You can write interleave in a way that directly encodes its intention. I think a interleave has a beautiful recursive definition. I think the use of deep destructuring assignment is nice here because the function's signature shows the shape of the data that interleave is expecting; an array of arrays. Mathematical induction allows us to naturally handle the branches of our program -
const None =
  Symbol ('None')
const interleave =
  ( [ [ v = None, ...vs ] = []  // first subarray
    , ...rest                   // rest of subarrays
    ]
  ) =>
    v === None
      ? rest.length === 0
        ? vs                                   // base: no `v`, no `rest`
        : interleave (rest)                    // inductive: some `rest`
      : [ v, ...interleave ([ ...rest, vs ]) ] // inductive: some `v`, some `rest`
const input =
  [ [ "one", "two", "three" ]
  , [ "uno", "dos" ]
  , [ "1", "2", "3", "4" ]
  , [ "first", "second", "third" ]
  ]
console.log (interleave (input))
// [ "one", "uno", "1", "first", "two", "dos", "2", "second", "three", "3", "third", "4" ]
 
 
interleave has released us from the shackles of close-minded thinking. I no longer need to think about my problem in terms of misshapen pieces that awkwardly fit together – I'm not thinking about array indexes, or sort, or forEach, or mutating state with push, or making comparisons using > or Math.max. Nor am I having to think about perverse things like array-like – wow, we really do take for granted just how much we've come to know about JavaScript!
Above, it should feel refreshing that there are no dependencies. Imagine a beginner approaching this program: he/she would only need to learn 1) how to define a function, 2) destructuring syntax, 3) ternary expressions. Programs cobbled together with countless small dependencies will require the learner to familiarize him/herself with each before an intuition for the program can be acquired.
That said, JavaScript syntaxes for destructuring values are not the most pretty and sometimes trades for convenience are made for increased readability -
const interleave = ([ v, ...vs ], acc = []) =>
  v === undefined
    ? acc
: isEmpty (v)
    ? interleave (vs, acc)
: interleave
    ( [ ...vs, tail (v) ]
    , [ ...acc, head (v) ]
    )
The dependencies that evolved here are isEmpty, tail, and head -
const isEmpty = xs =>
  xs.length === 0
const head = ([ x, ...xs ]) =>
  x
const tail = ([ x, ...xs ]) =>
  xs
Functionality is the same -
const input =
  [ [ "one", "two", "three" ]
  , [ "uno", "dos" ]
  , [ "1", "2", "3", "4" ]
  , [ "first", "second", "third" ]
  ]
console.log (interleave (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 interleave = ([ v, ...vs ], acc = []) =>
  v === undefined
    ? acc
: isEmpty (v)
    ? interleave (vs, acc)
: interleave
    ( [ ...vs, tail (v) ]
    , [ ...acc, head (v) ]
    )
const input =
  [ [ "one", "two", "three" ]
  , [ "uno", "dos" ]
  , [ "1", "2", "3", "4" ]
  , [ "first", "second", "third" ]
  ]
console.log (interleave (input))
// [ "one", "uno", "1", "first", "two", "dos", "2", "second", "three", "3", "third", "4" ]
 
 
If you start thinking about interleave by using map, filter, and reduce, then it's likely they will be a part of the final solution. If this is your approach, it should surprise you that map, filter, and reduce are nowhere to be seen in the two programs in this answer. The lesson here is you become a prisoner to what you know. You sometimes need to forget map and reduce in order to observe that other problems have a unique nature and thus a common approach, although potentially valid, is not necessarily the best fit.