NodeJS - 从数组中减去数组,而不是删除所有重复项[重复]
Posted
技术标签:
【中文标题】NodeJS - 从数组中减去数组,而不是删除所有重复项[重复]【英文标题】:NodeJS - Subtract array from array, not removing all duplicates [duplicate] 【发布时间】:2018-02-11 12:10:57 【问题描述】:标题可能没有多大意义,但你会怎么做:
a = [1, 2, 2, 3, 3, 3];
b = [1, 2, 3];
a.subtract(b);
我希望它返回 [2, 3, 3],而不是像类似问题的其他答案那样返回 [],它只保留根本不在另一个数组中的项目,而不是只删除多少在另一个数组中。
【问题讨论】:
所以只删除第一个出现的第二个数组项? 【参考方案1】:您可以为Array
创建一个原型,并通过检查并消除找到的元素来过滤数组。
Array.prototype.subtract = function (array)
array = array.slice();
return this.filter(function (a)
var p = array.indexOf(a);
if (p === -1)
return true;
array.splice(p, 1);
);
var a = [1, 2, 2, 3, 3, 3],
b = [1, 2, 3];
console.log(a.subtract(b));
带有哈希表的更快版本:
Array.prototype.subtract = function (array)
var hash = Object.create(null);
array.forEach(function (a)
hash[a] = (hash[a] || 0) + 1;
);
return this.filter(function (a)
return !hash[a] || (hash[a]--, false);
);
var a = [1, 2, 2, 3, 3, 3],
b = [1, 2, 3];
console.log(a.subtract(b));
【讨论】:
哇,真快。谢谢!我使用了类似但有点不同的东西,那就是删除所有重复项,而不是在第二个数组中每次出现一次,例如 Array.prototype.diff = function(a) return this.filter(function(i) return a.indexOf(i) 我用你更快的版本更新了my testbench。它仍然比使用Set
慢
但我的解决方案适用于 ES5。
@NinaScholz 无论如何都没关系,我之前的回答无效。我现在通过改进你的答案的性能进行了更新,当我更正我的答案并改用Map
时,它实际上比你的普通对象方法慢了 13%。【参考方案2】:
您可以使用filter()
和indexOf()
来检查元素是否存在于其他数组中,是否使用splice()
删除它
var a = [1, 2, 2, 3, 3, 3];
var b = [1, 2, 3];
var result = a.filter(function(e)
let i = b.indexOf(e)
return i == -1 ? true : (b.splice(i, 1), false)
)
console.log(result)
【讨论】:
我使用了类似的东西,但没有使用 splice 将其删除,而是将其全部从数组中删除(这是不对的): Array.prototype.diff = function(a) return this.filter(function(i) return a.indexOf(i) 【参考方案3】:您可以这样做:循环遍历数组b
并检查它是否存在于数组a
中。如果是这样,请在其索引处插入 undefined
。返回过滤后的a
数组以删除所有undefined
元素。
编辑:实际上不需要过滤器使用splice()
其他人已经这样做了。
let a = [1, 2, 2, 3, 3, 3];
let b = [1, 2, 3];
let sub = function(a, b)
for (i in b)
let index = a.indexOf(b[i]);
if (index != -1)
a.splice([index],1)
return a
console.log(sub(a, b))
【讨论】:
【参考方案4】:此处建议使用indexOf()
检查是否存在的答案是低效的 O(n^2) 运行时。更有效的方法是使用Map
并使用has()
检查是否存在,从而将运行时间降低到 O(n):
// see the bottom of the answer for a more efficient implementation than this
Object.defineProperty(Array.prototype, 'subtract',
configurable: true,
value: function subtract (array)
return this.filter(
function (element)
const count = this.get(element)
if (count > 0)
this.set(element, count - 1)
return count === 0
, array.reduce(
(map, element) =>
if (map.has(element))
map.set(element, map.get(element) + 1)
else
map.set(element, 1)
return map
, new Map()
)
)
,
writable: true
)
let a = [1, 2, 2, 3, 3, 3]
let b = [1, 2, 3]
console.log(a.subtract(b))
这是一个测试平台,与@Nina 的答案相比,显示了此解决方案的效率:
Array.prototype.ninaSubtract = function (array)
var hash = Object.create(null);
array.forEach(function (a)
hash[a] = (hash[a] || 0) + 1;
);
return this.filter(function (a)
return !hash[a] || (hash[a]--, false);
);
Object.defineProperty(Array.prototype, 'patrickSubtract',
configurable: true,
value: function subtract (array)
return this.filter(
function (element)
const count = this.get(element)
if (count > 0)
this.set(element, count - 1)
return count === 0
, array.reduce(
(map, element) =>
if (map.has(element))
map.set(element, map.get(element) + 1)
else
map.set(element, 1)
return map
, new Map()
)
)
,
writable: true
)
let a = [1, 2, 2, 3, 3, 3]
let b = [1, 2, 3]
let ninaStart = 0
let ninaStop = 0
let patrickStart = 0
let patrickStop = 0
ninaStart = performance.now()
for (let i = 100000; i > 0; i--)
a.ninaSubtract(b)
ninaStop = performance.now()
patrickStart = performance.now()
for (let i = 100000; i > 0; i--)
a.patrickSubtract(b)
patrickStop = performance.now()
console.log('Nina time: ', ninaStop - ninaStart, 'ms')
console.log('Patrick time: ', patrickStop - patrickStart, 'ms')
在我的笔记本电脑上使用 Chrome v60 运行几次表明 Nina 使用普通对象的更新答案比我的答案快大约 13%。
让我们尝试通过改进她的答案来解决这个问题:
Array.prototype.ninaSubtract = function (array)
var hash = Object.create(null);
array.forEach(function (a)
hash[a] = (hash[a] || 0) + 1;
);
return this.filter(function (a)
return !hash[a] || (hash[a]--, false);
);
Object.defineProperty(Array.prototype, 'patrickSubtract',
configurable: true,
value: function subtract (array)
const lookup = Object.create(null)
const output = []
for (let index = 0; index < array.length; index++)
const element = array[index]
lookup[element] = element in lookup ? lookup[element] + 1 : 1
for (let index = 0; index < this.length; index++)
const element = this[index]
if (!(element in lookup) || lookup[element]-- <= 0)
output.push(element)
return output
,
writable: true
)
let a = [1, 2, 2, 3, 3, 3]
let b = [1, 2, 3]
let ninaStart = 0
let ninaStop = 0
let patrickStart = 0
let patrickStop = 0
ninaStart = performance.now()
for (let i = 100000; i > 0; i--)
a.ninaSubtract(b)
ninaStop = performance.now()
patrickStart = performance.now()
for (let i = 100000; i > 0; i--)
a.patrickSubtract(b)
patrickStop = performance.now()
console.log('Nina time: ', ninaStop - ninaStart, 'ms')
console.log('Patrick time: ', patrickStop - patrickStart, 'ms')
虽然不那么规范,但它的性能要高得多,因为它避免使用 reduce()
、forEach()
或 filter()
来减少函数调用的上下文切换。此解决方案的执行时间几乎是 Nina 更新答案的一半(大约快 43%):
Object.defineProperty(Array.prototype, 'subtract',
configurable: true,
value: function subtract (array)
const lookup = Object.create(null)
const output = []
for (let index = 0; index < array.length; index++)
const element = array[index]
lookup[element] = element in lookup ? lookup[element] + 1 : 1
for (let index = 0; index < this.length; index++)
const element = this[index]
if (!(element in lookup) || lookup[element]-- <= 0)
output.push(element)
return output
,
writable: true
)
let a = [1, 2, 2, 3, 3, 3]
let b = [1, 2, 3]
console.log(a.subtract(b))
更新
之前出现性能差异的原因是因为我之前使用Set
的算法不正确。如果b
包含非唯一元素,则输出将不正确。
【讨论】:
以上是关于NodeJS - 从数组中减去数组,而不是删除所有重复项[重复]的主要内容,如果未能解决你的问题,请参考以下文章
.splice() 正在从数组中删除 2 个对象而不是 1 个