合并两个具有交替值的数组
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=array1
、y=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)
));
我个人认为后一种方法不太灵活,因为它只能解决这个问题。对所有数组进行并行顺序遍历可以重新用于其他事情,例如压缩数组,因此让辅助函数消耗它的输出似乎可以留下更多选项。另一方面,只有一个函数可以更直接地了解它是如何实现的。
【讨论】:
以上是关于合并两个具有交替值的数组的主要内容,如果未能解决你的问题,请参考以下文章