i would like to merge 2 arrays with a different length:
let array2 = [\"a\", \"b\", \"c\", \"d\"];
let array2 = [1, 2];
let outcome = [\"a\",1 ,\"b\", 2, \"
Using ES6 generator functions this can be implemented generically for any amount of arrays of any lengths. The key is going through all arrays regardless of length in order and then adding each of the values they have into a single merged array.
By using the iterator protocol of arrays we can uniformly proceed through the items in each array.
When producing some sequence of alternating values of other sequences, that is frequently called an interleave. Sometimes also called a Faro shuffle - it's more widely known with playing cards - a perfect Faro shuffle combines two piles of cards in such a way that cards from each pile alternate. However, this is an example of an interleave sequence and mathematicians also use the term to describe the process of interleaving.
//go through all arrays and produce their values
function* parallelWalkAllArrays(...arrays) {
//get iterator for each array
const iterators = arrays.map(arr => arr[Symbol.iterator]());
let values;
//loop until complete
while (true) {
values = iterators
.map(it => it.next()) //addvance iterators
.filter(({done}) => !done) //keep anything that is not finished
.map(({value}) => value); //get the values
//quit if all are exhausted
if (values.length === 0)
return;
//yield a tuple of all values
yield values;
}
}
function interleaveMergeArrays(...arrays) {
//start a generator function
const sequence = parallelWalkAllArrays(...arrays);
let merged = [];
//flatten each result into a single array
for (const result of sequence) {
merged.push(...result)
}
return merged;
}
const array1 = [1, 2, 3, 4, 5];
const array2 = ['a', 'b', 'c', 'd', 'e'];
console.log(
interleaveMergeArrays(array1, array2)
);
const shortArray = ["apple", "banana"];
console.log(
interleaveMergeArrays(array1, shortArray)
);
console.log(
interleaveMergeArrays(shortArray, array2)
);
console.log(
interleaveMergeArrays(array1, shortArray, array2)
);
Alternatively, you can take a very similar approach but directly produce a flat sequence from the generator. That way you can consume it immediately.
//go through all arrays and produce their values
function* walkAllArrays(...arrays) {
//get iterator for each array
const iterators = arrays.map(arr => arr[Symbol.iterator]());
let values;
//loop until complete
while (true) {
values = iterators
.map(it => it.next()) //addvance iterators
.filter(({done}) => !done) //keep anything that is not finished
.map(({value}) => value); //get the values
//quit if all are exhausted
if (values.length === 0)
return;
//yield each value
for (const value of values)
yield value;
}
}
const array1 = [1, 2, 3, 4, 5];
const array2 = ['a', 'b', 'c', 'd', 'e'];
console.log(Array.from(
walkAllArrays(array1, array2)
));
const shortArray = ["apple", "banana"];
console.log(Array.from(
walkAllArrays(array1, shortArray)
));
console.log(Array.from(
walkAllArrays(shortArray, array2)
));
console.log(Array.from(
walkAllArrays(array1, shortArray, array2)
));
I personally find the latter approach less flexible, as it only solves this problem. Doing a parallel sequential walk through all arrays can be re-used for other things such as zipping arrays, so having a helper function consume the output of that seems like it can leave more options open. On the other hand having a single function makes it a bit more straightforward to see how it's implemented.
You could iterate the min length of both array and build alternate elements and at the end push the rest.
var array1 = ["a", "b", "c", "d"],
array2 = [1, 2],
result = [],
i, l = Math.min(array1.length, array2.length);
for (i = 0; i < l; i++) {
result.push(array1[i], array2[i]);
}
result.push(...array1.slice(l), ...array2.slice(l));
console.log(result);
Solution for an arbitrary count of arrays with a transposing algorithm and later flattening.
var array1 = ["a", "b", "c", "d"],
array2 = [1, 2],
result = [array1, array2]
.reduce((r, a) => (a.forEach((a, i) => (r[i] = r[i] || []).push(a)), r), [])
.reduce((a, b) => a.concat(b));
console.log(result);
Here's another way you can do it using destructuring assignment -
const interleave = ([ x, ...xs ], ys = []) =>
x === undefined
? ys // base: no x
: [ x, ...interleave (ys, xs) ] // inductive: some x
console.log (interleave ([0, 2, 4, 6], [1, 3, 5])) // [ 0 1 2 3 4 5 6 ]
console.log (interleave ([0, 2, 4], [1, 3, 5, 7])) // [ 0 1 2 3 4 5 7 ]
console.log (interleave ([0, 2, 4], [])) // [ 0 2 4 ]
console.log (interleave ([], [1, 3, 5, 7])) // [ 1 3 5 7 ]
console.log (interleave ([], [])) // [ ]
And another variation that supports any number of input arrays -
const interleave = ([ x, ...xs ], ...rest) =>
x === undefined
? rest.length === 0
? [] // base: no x, no rest
: interleave (...rest) // inductive: no x, some rest
: [ x, ...interleave(...rest, xs) ] // inductive: some x, some rest
console.log (interleave ([0, 2, 4, 6], [1, 3, 5])) // [ 0 1 2 3 4 5 6 ]
console.log (interleave ([0, 2, 4], [1, 3, 5, 7])) // [ 0 1 2 3 4 5 7 ]
console.log (interleave ([0, 2, 4], [])) // [ 0 2 4 ]
console.log (interleave ([], [1, 3, 5, 7])) // [ 1 3 5 7 ]
console.log (interleave ([], [])) // [ ]