合并两个具有交替值的数组

Posted

技术标签:

【中文标题】合并两个具有交替值的数组【英文标题】: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) 那么你尝试了什么?循环最短附加两个,连接剩余的......似乎很简单 【参考方案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);

解释:

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

【讨论】:

【参考方案13】:

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

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

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

    6.7ms 9.8ms 16.7ms 23.3ms 24.2ms 151.7ms 297.8ms 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)
));

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

【讨论】:

以上是关于合并两个具有交替值的数组的主要内容,如果未能解决你的问题,请参考以下文章

合并两个数组,使值交替

如何合并列中具有相同值的两个表

合并两个具有聚合列值的数据框作为结果

将两个具有相似列值的数据框合并在一起[重复]

Pandas:合并具有不同索引和缺失值的两个数据框

尝试使用过滤器设置具有唯一值的数组失败