Merge two arrays with alternating Values

前端 未结 9 1992
南笙
南笙 2020-12-03 14:19

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, \"         


        
相关标签:
9条回答
  • 2020-12-03 15:13

    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.

    0 讨论(0)
  • 2020-12-03 15:15

    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);

    0 讨论(0)
  • 2020-12-03 15:15

    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 ([], []))                  // [ ]

    0 讨论(0)
提交回复
热议问题