【问题标题】:Merge two arrays with alternating values合并两个具有交替值的数组
【发布时间】:2018-04-14 03:25:24
【问题描述】:

我想合并两个长度不同的数组:

let array1 = ["a", "b", "c", "d"];
let array2 = [1, 2];

我期望的结果是["a", 1 ,"b", 2, "c", "d"]

最好的方法是什么?

【问题讨论】:

  • 最好的方法是什么”是什么意思?预期的结果是什么? “合并”是一个相当宽泛的概念。
  • 你能写出最终结果应该是什么样子吗?
  • 通过交替值你的意思是像 array1 中的 1 然后 array2 中的一个等等?
  • 你可以简单地使用array1.concat(array2)
  • 那么你尝试了什么?循环最短附加两个,连接剩余的......似乎很简单

标签: javascript ecmascript-6 functional-programming


【解决方案1】:

这是使用解构赋值的另一种方法 -

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

另一个支持任意数量输入数组的变体 -

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

【讨论】:

    【解决方案2】:

    您可以迭代两个数组的最小长度并构建备用元素,最后推送其余元素。

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

    使用转置算法和随后的展平解决任意数量的数组。

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

    【讨论】:

      【解决方案3】:

      创建一个元组数组。每个元组包含每个数组的 1 个元素,通过展开元组数组并添加数组中的剩余项来展平:

      const a1 = ["a", "b", "c", "d"];
      const a2 = [1,2];
      const l = Math.min(a1.length, a2.length);
      
      const merged = [].concat(...Array.from({ length: l }, (_, i) => [a1[i], a2[i]]), a1.slice(l), a2.slice(l));
        
      console.log(merged);

      【讨论】:

      • Array.from() 和 .filter() 需要 4.044921875ms 和 result.push() 需要 3.296630859375ms
      【解决方案4】:

      这是一个采用任意数量数组的现代解决方案:

      const braidArrays = (...arrays) => {
        const braided = [];
        for (let i = 0; i < Math.max(...arrays.map(a => a.length)); i++) {
          arrays.forEach((array) => {
            if (array[i] !== undefined) braided.push(array[i]);
          });
        }
        return braided;
      };
      

      请注意,您可以将 Math.max 更改为 Math.min 以仅包含最短的数组。

      这是一个示例 I/O:

      braidArrays(['a','b','c','d'], [1,2,3], [99,98,97,96,95]);
      // ['a', 1, 99, 'b', 2, 98, 'c', 3, 97, 'd', 96, 95]
      

      【讨论】:

        【解决方案5】:

        ONELINER:我假设x=array1y=array2、x 和 y 可以是任意的 arr

        [...x,...y].reduce((l,c,i)=>(i<x.length&&l.push(x[i]),i<y.length&&l.push(y[i]),l),[])
        

        工作example(3 例)

        【讨论】:

          【解决方案6】:

          你可以这样做:

          const array1 = ["a", "b", "c", "d"];
          const array2 = [1, 2];
          const mergeArrays = (a, b) => (a.length > b.length ? a : b)
            .reduce((acc, cur, i) => a[i] && b[i] ? [...acc, a[i], b[i]] : [...acc, cur], []);
          
          console.log(mergeArrays(array1, array2)); // ["a",1 ,"b", 2, "c", "d"]

          【讨论】:

            【解决方案7】:

            这可以通过使用reduce 中的拼接函数来完成:

            function splicer(array, element, index) {
                array.splice(index * 2, 0, element);
                return array;
            }
            
            function weave(array1, array2) {
                return array1.reduce(splicer, array2.slice());
            }
            
            let array1 = ["a", "b", "c", "d"];
            let array2 = [1, 2];
            
            let outcome = weave(array1, array2);
            
            console.log(outcome);

            【讨论】:

              【解决方案8】:

              一个有点冗长的解决方案,让您选择哪个数组在前

              const a = ['a', 'b', 'c'];
              const b = [1, 4];
              const combineAlternatingArrays = (a, b) => {
                let combined = [];
                const [shorter, larger] = [a, b].sort((a, b) => a.length -b.length);
              
                shorter.forEach((item, i) => {
                  combined.push(larger[i], item);
                })
                combined.push(...larger.slice(shorter.length));
              
                return combined;
              }
              console.log(combineAlternatingArrays(a, b));
              

              也可以使用reduce,但我认为语法不太清楚。

              const a = ['a', 'b', 'c'];
              const b = [1, 4];
              const combineAlternatingArrays = (a, b) => {
                const [shorter, larger] = [a, b].sort((a, b) => a.length -b.length);
              
                return shorter.reduce(
                  (combined, next, i, shorter) => {
                    return (i === (shorter.length -1)) ? [...combined, larger[i], next, ...larger.slice(shorter.length)] : [...combined, larger[i], next];
                  },
                  []
                );
              }
              console.log(combineAlternatingArrays(a, b));
              

              【讨论】:

                【解决方案9】:

                对于这样的场景,我一般使用nullish coalescing operator (??)

                var mergeAlternately = (a, b) => {
                  const maxLength = Math.max(a.length, b.length);
                  let result = [];
                  
                  for (let i = 0; i < maxLength; i++) {
                    result.push( (a[i] ?? '') , (b[i] ?? ''));
                  } 
                  // Remove empty array values
                  return result.filter(item => item);
                };
                
                let array1 = ["a", "b", "c", "d"];
                let array2 = [1, 2];
                
                console.log(mergeAlternately(array1, array2))

                【讨论】:

                  【解决方案10】:

                  更现代、更高效、更短的方式:

                  const arr1 = ["a", "b", "c", "d"]
                  const arr2 = [1, 2]
                  
                  const res = (arr1.length > arr2.length ? arr1 : arr2) // you can replace it with just arr1, if you know its always longer
                              .flatMap((e, idx) => arr2[idx] ? [e, arr2[idx]] : [e])
                  console.log(res)

                  【讨论】:

                    【解决方案11】:

                    使用迭代器:

                    function *gen(arr1, arr2){
                        for(let i = 0; i < Math.max(arr1.length, arr2.length); i++) {
                            if (arr1[i]) yield arr1[i];
                            if (arr2[i]) yield arr2[i];
                        }
                    }
                    const x = gen(['a','b','c','d'], [1,2]);
                    const result = [...x];
                    

                    给予

                    Array(6) [ "a", 1, "b", 2, "c", "d" ]
                    

                    【讨论】:

                      【解决方案12】:

                      另一个ONELINER

                      const merge = (arr1, arr2) => ((arr1.length > arr2.length) ? arr1 : arr2).map((_,i)=>[arr1[i],arr2[i]]).flat().filter(Boolean);
                      

                      解释:

                      1. 用三元条件运算符取最长数组
                      2. 使用 map 为每个索引创建来自每个数组的一对元素
                      3. 展平结果
                      4. 删除未定义的

                      【讨论】:

                        【解决方案13】:

                        如果有人在寻找性能比较,我已经做了一个比较上述一些功能的文件。

                        测试是合并两个长度为 200 和 500 的数组。对于每种方法,测试运行 1000 次。

                        以下是按最快(时间)排序的结果:

                        1. 6.7ms
                        2. 9.8ms
                        3. 16.7ms
                        4. 23.3ms
                        5. 24.2ms
                        6. 151.7ms
                        7. 297.8ms
                        8. 1.15s

                        链接到file

                        【讨论】:

                          【解决方案14】:

                          使用 ES6 generator functions 这可以通用地实现任意数量的任意长度的数组。关键是按顺序遍历所有数组,无论长度如何,然后将它们拥有的每个值添加到一个合并的数组中。

                          通过使用数组的iterator protocol,我们可以统一处理每个数组中的项目。

                          当产生其他序列的一些交替值序列时,通常称为interleave。有时也称为Faro shuffle - 它在扑克牌中更广为人知 - 完美的法鲁洗牌将两堆纸牌组合在一起,使每叠纸牌交替出现。然而,这是一个交错序列的例子,数学家也用这个术语来描述交错的过程。

                          //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())      //advance 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)
                          );

                          或者,您可以采用非常相似的方法,但直接从生成器生成平面序列。这样您就可以立即食用。

                          //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())      //advance 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)
                          ));

                          我个人认为后一种方法不太灵活,因为它只能解决这个问题。对所有数组进行并行顺序遍历可以重新用于其他事情,例如压缩数组,因此让辅助函数消耗它的输出似乎可以留下更多选项。另一方面,只有一个函数可以更直接地了解它是如何实现的。

                          【讨论】:

                            猜你喜欢
                            • 2012-10-26
                            • 1970-01-01
                            • 1970-01-01
                            • 1970-01-01
                            • 1970-01-01
                            • 1970-01-01
                            • 2015-03-07
                            相关资源
                            最近更新 更多