leo
leo

Reputation: 8520

Efficient way to mix arrays

Given a number of different sized Arrays:

let allArrays = [ [1, 2, 3], [a, b], [100, 101, 102, 103] ]

I want to take the first element from each array, then the second element from each array, and so on:

let finalArray = [ 1, a, 100, 2, b, 101, 3, 102, 103 ]

My current, naïve solution is to simply loop through allArrays until allArrays.flat() is empty, building a new array on the way:

          let arr = [ [1, 2, 3], ['a', 'b'], [100, 101, 102, 103] ]
          const new_arr = []
          while (arr.flat().length) {
            for (let sub_arr of arr) {
              if (sub_arr.length) {
                new_arr.push(sub_arr.shift())
              }
            }
          }
          new_arr
          // [ 1, 'a', 100, 2, 'b', 101, 3, 102, 103 ]

This strikes me a quite inefficient. What would be the best way to go about this?

Edit to clarify what I'm looking for: Some of the arrays can sometimes be thousands of elements long, and the code is run in the frontend, so I'm searching for a method that is fast.

Also, the lengths and combinations of lengths vary considerably, so rather than optimizing for a single scenario, I'm looking for a way that will be guaranteed to be reasonably fast no matter how the arrays look.

Upvotes: 0

Views: 90

Answers (2)

marzelin
marzelin

Reputation: 11600

If arrays are mostly the same length, find max length and iterate over the arrays:

let
  arrays = [
    [1, 2, 3],
    ['a', 'b'],
    [100, 101, 102, 103]
  ];
let maxLength = 0;
let result = [];
for (let i = 0; i < arrays.length; i++) {
  let len = arrays[i].length;
  if (len) {
    maxLength < len && (maxLength = len);
    result.push(arrays[i][0]);
  }
}
for (let i = 1; i < maxLength; i++) {
  for (let j = 0; j < arrays.length; j++) {
    if (i < arrays[j].length) {
      result.push(arrays[j][i]);
    }
  }
}

console.log(...result);

If arrays are of much different lengths, create an auxiliary array with arrays' indices that have at least a given length:

let
  arrays = [
    [1, 2, 3],
    ['a', 'b'],
    [100, 101, 102, 103]
  ];
let arrsToIterate = [];
let result = [];
for (let i = 0; i < arrays.length; i++) {
  let len = arrays[i].length;
  if (len) {
    result.push(arrays[i][0]);
    if (len > 1) {
      arrsToIterate.push(i);
    }
  }

}
let idx = 1;
while (arrsToIterate.length) {
  for (let i = 0; i < arrsToIterate.length; i++) {
    let arr = arrays[arrsToIterate[i]];
    if (idx < arr.length) {
      result.push(arr[idx]);
    } else {
      arrsToIterate.splice(i, 1);
      i--;
    }
  }
  idx++;
}
console.log(...result);

Upvotes: 1

Nina Scholz
Nina Scholz

Reputation: 386560

You could reduce the arrays and flat later.

let
    arrays = [[1, 2, 3], ['a', 'b'], [100, 101, 102, 103]]
    result = arrays
        .reduce((r, array) => {
            array.forEach((v, i) => (r[i] ??= []).push(v));
            return r;
        }, [])
        .flat();

console.log(...result);

Upvotes: 3

Related Questions