简体   繁体   English

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

[英]Merge two arrays with alternating values

I would like to merge 2 arrays with a different length:我想合并 2 个不同长度的数组:

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

The outcome I would expect is ["a", 1 ,"b", 2, "c", "d"]我期望的结果是["a", 1 ,"b", 2, "c", "d"]

What's the best way to do that?最好的方法是什么?

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

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

Create an array of tuples.创建一个元组数组。 Each tuple contains 1 element from each array, flatten by spreading the array of tuples, and adding the leftover items from the arrays:每个元组包含来自每个数组的 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);

Here's a modern solution that takes any number of arrays:这是一个采用任意数量数组的现代解决方案:

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

Note that you could change Math.max to Math.min to only include up to the shortest array.请注意,您可以将Math.max更改为Math.min以仅包含最短的数组。

Here's a sample I/O:这是一个示例 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]

ONELINER : I assume that x=array1 , y=array2 , x and y can be arbitrary arr 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),[])

working example (for 3 cases)工作示例(3个案例)

You can do:你可以做:

 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"]

This can be done rather simply using a splicing function within reduce :这可以通过使用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);

A bit verbose solution that lets you choose which array goes first有点冗长的解决方案,可让您选择首先使用哪个数组

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

It is also possible to use a reduce, but the syntax is less clear in my opinnion.也可以使用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));

Using ES6 generator functions this can be implemented generically for any amount of arrays of any lengths.使用 ES6 生成器函数,这可以针对任意数量的任意长度的数组进行通用实现。 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 .当产生其他序列的一些交替值序列时,这通常称为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.有时也称为Faro shuffle - 它在扑克牌中更广为人知 - 完美的 Faro shuffle 将两堆纸牌组合在一起,每堆纸牌交替进行。 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.另一方面,只有一个函数可以更直接地了解它是如何实现的。

I generally use nullish coalescing operator (??) for such a scenario:对于这种情况,我通常使用空合并运算符 (??)

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

More modern, efficient and shorter way:更现代、更高效、更短的方式:

 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)

using an iterator:使用迭代器:

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

gives

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

Another ONELINER :另一个ONELINER

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

explanation:解释:

  1. Take the longest array with the ternary conditional operator用三元条件运算符取最长的数组
  2. Use map to create for each index a pair of elements from each array使用 map 为每个索引创建来自每个数组的一对元素
  3. Flatten the result展平结果
  4. Remove the undefined删除未定义的

In case someone is looking for a performance comparison i have done a file which compares some of the above functions.如果有人正在寻找性能比较,我已经完成了一个比较上述某些功能的文件。

The test was to merge two arrays with lengths 200 and 500. For each method the test was run 1000 times.测试是合并两个长度为 200 和 500 的数组。对于每种方法,测试运行 1000 次。

Here are the results ordered by the fastest (time):以下是按最快(时间)排序的结果:

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

Link to the file 文件链接

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM