2

I'm confused with the solution to this task. I have two arrays:

const arr1 = [ 1,   2,   3,   4, 5, 6, 7, 8];
const arr2 = ['a', 'b', 'c', 'd'];

What do I want to get after merging these arrays:

[1, 'a', 'b', 2, 3, 'c', 'd', 4, 5, 6, 7, 8];

Graphically it looks like a snake

image

It has to work also in the case when the length of the first array is shorter than the second one. For example, I have two arrays:

const arr1 = [ 1,   2,   3,   4];
const arr2 = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];

And I want to get after merging:

[1, 'a', 'b', 2, 3, 'c', 'd', 4, 'e', 'f', 'g'];

(image)

jo_va
  • 13,504
  • 3
  • 23
  • 47
vlancv
  • 41
  • 5
  • 3
    what is your criteria for merging? – kemotoe Mar 07 '19 at 16:07
  • 3
    Would you post your code, please? – danielarend Mar 07 '19 at 16:07
  • 5
    What is the logic used for the positions in the result? – Barmar Mar 07 '19 at 16:09
  • Except for the `1` at the beginning, it looks like you just take 2 elements at a time from each array, until you run out. Is that correct? – Barmar Mar 07 '19 at 16:11
  • Merging can happen by checking if the element index is even or odd, then picking (array1, array2) or (array2, array1) based on that. I'll stop here because the task looks a lot like a student assignment, which I don't want to solve completely. :) – Miro J. Mar 07 '19 at 16:21

5 Answers5

1

function reorderArrays(array1, array2){
  let newArray = [];
  
  while(array1.length || array2.length) {
      array1.length && newArray.push(array1.shift());
      array2.length && newArray.push(array2.shift());
      array2.length && newArray.push(array2.shift());
      array1.length && newArray.push(array1.shift());
  }
  return newArray;
}

console.log(reorderArrays([1, 2, 3, 4, 5, 6, 7, 8], ['a', 'b', 'c', 'd']));
console.log(reorderArrays([ 1,   2,   3,   4], ['a', 'b', 'c', 'd', 'e', 'f', 'g']));

There is room for optimization (for instance, if !length of one array, just push the rest of the other array to your new array)

This solution works for undefined edge cases.

Quentin C
  • 1,739
  • 14
  • 26
  • 2
    I like this method except that it destroys the original arrays, which is a very unexpected side effect. Also, you can just get rid of the initial `array1` push by moving it into the `while` loop and ordering it as `array1`, `array2`, `array2`, `array1`. – p.s.w.g Mar 07 '19 at 16:36
  • Thanks for comment. I edited, for the destroying, you could just copy the arrays in the function – Quentin C Mar 07 '19 at 16:40
  • 1
    Actually you are right, cloning arrays is not this straightforward in js. If the author wants to keep original arrays, there is this post: https://stackoverflow.com/questions/3978492/fastest-way-to-duplicate-an-array-in-javascript-slice-vs-for-loop – Quentin C Mar 07 '19 at 16:47
  • 1
    Thank you. It works perfectly and looks beautiful. – vlancv Mar 07 '19 at 17:17
1

Here is a short and immutable way to do it:

const snake = (a, b) => Array.from( // create an array
    { length: Math.max(a.length, b.length) }, // as long as the longest of a and b
    (_, i) => i % 2 ? [b[i], a[i]] : [a[i], b[i]] // fill it with alternating pairs from a and b
  ).flat().filter(x => x !== undefined); // then flatten the pairs and remove undefined entries

console.log(...snake([1, 2, 3, 4, 5, 6, 7, 8], ['a', 'b', 'c', 'd']));
console.log(...snake([1, 2, 3, 4], ['a', 'b', 'c', 'd', 'e', 'f', 'g']));
console.log(...snake([1, 2, 4, 5], []));
console.log(...snake([], ['a', 'b', 'c', 'd']));

The steps are:

  1. Create an array as long as your longest array using Array.from()
  2. Fill it with your first array, if it is shorter, the rest will be padded with undefined
  3. Use the second argument of Array.from() or use map() to transform the entries into pairs taken from both arrays, alternate the order of the pairs by checking if the index is even or odd
  4. Flatten the result using Array.flat()
  5. Filter-out the undefined using Array.filter()
jo_va
  • 13,504
  • 3
  • 23
  • 47
0

Here's one solution using flatMap:

const merge = (arr1, arr2) => {
  return arr1.flatMap((x, i) => {
    const items = [x];
    if (i % 2 === 0) {
      if (i < arr2.length) {
        items.push(arr2[i]);
      }
      if (i + 1 < arr2.length) {
        items.push(arr2[i + 1]);
      }
    }
    if (i + 1 === arr1.length) {
      items.push(...arr2.slice(i + 1));
    }
    return items;
  });
}

console.log(merge([1, 2, 3, 4, 5, 6, 7, 8], ['a', 'b', 'c', 'd']));    // [1, "a", "b", 2, 3, "c", "d", 4, 5, 6, 7, 8]

console.log(merge([1, 2, 3, 4], ['a', 'b', 'c', 'd', 'e', 'f', 'g'])); // [1, "a", "b", 2, 3, "c", "d", 4, "e", "f", "g"]

Explanation:

The function that you pass to flatMap will return a array of elements to include in the result array in order. The array is determined by looping over flatMap in the following way:

  1. Each element is directly included in the result ([x])
  2. For each even numbered index (i % 2 === 0), insert up to two elements from arr2 (.push(arr2[i]), .push(arr2[i + 1])).
  3. If you're at the end of arr1 (i + 1 === arr1.length), insert all the remaining items from arr2 (.push(...arr2.slice(i + 1)))
p.s.w.g
  • 146,324
  • 30
  • 291
  • 331
0

Here you have a recursive approach based on this understanding of what you want:

Form first array: take 1 item, then 2 items, then 1 item, then 2 items and so on. From the second array take always 2 items.

To generalize, the method will receive two extra arrays that will determine the cycle of elements to take from each array you want to merge:

const customMerge = (a, b, toTakeA=[1], toTakeB=[1], res=[], i=0, j=0) =>
{
    if (!a.length)
        return res.concat(b);
    else if (!b.length)
        return res.concat(a);

    return customMerge(
        a.slice(toTakeA[i]),
        b.slice(toTakeB[j]),
        toTakeA,
        toTakeB,
        res.concat(a.slice(0, toTakeA[i])).concat(b.slice(0, toTakeB[j])),
        i >= toTakeA.length - 1 ? 0 : i + 1,
        j >= toTakeB.length - 1 ? 0 : j + 1
    );
}

console.log("First OP merge: ", customMerge(
    [1, 2, 3, 4, 5, 6, 7, 8],
    ['a', 'b', 'c', 'd'],
    [1, 2],
    [2]
));

console.log("Second OP merge: ", customMerge(
    [1, 2, 3, 4],
    ['a', 'b', 'c', 'd', 'e', 'f', 'g'],
    [1, 2],
    [2]
));

// Another custom merge.
console.log("A custom merge: ", customMerge(
    [1, 2, 3, 4, 5, 6, 7, 8, 9],
    ['a', 'b', 'c', 'd', 'e', 'f', 'g'],
    [1, 1, 2],
    [2, 1]
));

// Simple merge, 1 and 1.
console.log("Single 1 and 1 merge: ", customMerge(
    [1, 2, 3, 4, 5, 6, 7, 8, 9],
    ['a', 'b', 'c', 'd', 'e', 'f', 'g']
));
.as-console {background-color:black !important; color:lime;}
.as-console-wrapper {max-height:100% !important; top:0;}

The approach mostly uses Array.slice() and Array.concat() in case you need to read documentation about they.

Shidersz
  • 16,846
  • 2
  • 23
  • 48
0

You could take an offset and the extra element of the first array first and then take always two elements from both arrays. This apporach does not mutate the original arrays.

function getSnaked(a, b) {
    var result = [],
        l = Math.min(a.length, b.length),
        i = 0;

    result.push(...a.slice(0, 1));
    while (i < l) {
        result.push(...b.slice(i, ++i + 1));
        result.push(...a.slice(i, ++i + 1));
    }
    result.push(...b.slice(i));
    result.push(...a.slice(i + 1));
    return result;
}

console.log(...getSnaked([1, 2, 3, 4], ['a', 'b', 'c', 'd', 'e', 'f', 'g']));
console.log(...getSnaked([1, 2, 3, 4, 5, 6, 7, 8], ['a', 'b', 'c']));
console.log(...getSnaked([1, 2, 3, 4], []));
console.log(...getSnaked([], ['a', 'b', 'c', 'd', 'e', 'f', 'g']));
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392