你说起你的起点是一个数组,但最高效的方法是使用Map
而不是数组,其中键是名称,值是价格或包含价格的对象(取决于是否需要其他信息)。
使用未排序的数组
但如果你要使用数组进行操作,除非我们可以建立/维护排序后的数组(请参见下面的“使用排序后的数组”),否则没有比循环遍历它来查找带有给定name
的前一个元素更有效率的方法。对此,filter
不是正确的工具(您不需要它创建的数组)。你可以编写自己的循环:
let element;
for (let index = 0, length = array.length; index < length; ++index) {
const thisElement = array[index];
if (thisElement.name === name) {
element = thisElement;
break;
}
}
if (element) {
element.price += price;
} else {
array.push({name, price});
}
有些JavaScript引擎,如果你在循环之前声明了index
、length
和thisElement
,可能会获得更多的速度提升,哪怕只是那么一点点。
let element, index, length, thisElement;
for (index = 0, length = array.length; index < length; ++index) {
thisElement = array[index];
// ...
但对于其他情况可能会相反。(无论哪种方式,差异都不大。)
或者使用find
方法:
const element = array.find(e => e.name === name);
if (element) {
element.price += price;
} else {
array.push({name, price});
}
任何一种数据结构都可以提供线性查找时间。但如果您使用的是
Map
,则可以获得亚线性查找时间。
使用Map
如果将对象用作值:
const element = map.get(name);
if (element) {
element.price += price;
} else {
map.set(name, {name, price});
}
或者如果将价格作为值:
const currentPrice = map.get(name) ?? 0; // If not found, `get` returns undefined; convert it to 0
map.set(currentPrice + price);
使用排序数组
如果我们可以构建 / 维护排序顺序的数组(您已经说过您不能,但或许其他人在以后找到这篇文章时可以),我们可以通过使用二分查找来实现比线性查找更好的效果(代价是插入新元素时会有稍微更多的开销,因为插入点之后的所有元素都必须移动)。虽然需要编写更多的代码,但如果搜索时间是主要问题,则可以缩短搜索时间。
const upsert = (array, name, price) => {
let left = 0;
let right = array.length;
while (left < right) {
let guess = Math.floor((left + right) / 2);
let element = array[guess];
if (element.name === name) {
element.price += price;
return;
}
if (element.name < name) {
left = guess + 1;
} else {
right = guess - 1;
}
}
array.splice(left, 0, {name, price});
};