更新对象数组中的对象属性的最有效方法

5

我想知道在存储了10k+个元素的数组中更新对象属性的最高效方法。

例如,如果我有一个包含如下对象{name:"", price:""}的数组。

我想要替换或者更像是更新价格的值,如果数组已经包含该元素。

检查数组是否包含名称为x的对象,如果是,则将价格替换为最新价格。

我不想在那个数组中有重复的元素,因此它不会变得太大。我认为如果已经存在一个属性值,应该对其进行更新。

到目前为止,我尝试了几种方法,比如使用indexOf、splice或仅使用for循环。我想知道处理大型数组的性能最佳的方式是什么。

let array = [
{name:"abc", price: 24},
{name:"cde", price: 25},
{name:"fgh", price: 22},
{name:"gfds", price: 21},
]

function addToArray(elem){
  //check if array contains elem by checking for name property value
  if(array.filter(el => el.name === elem.name).length > 0){
    //update the array element that has the name of the passed elem
  }
}


我的意思是,基本上是一个简单的循环,但问题相当模糊和开放。也许如果您能给我们举几个数组元素的例子以及您想要对它们进行的更改,那就好了。 - T.J. Crowder
我以为我已经给出了元素结构,但我编辑了初始帖子,这有帮助吗? - BitQueen
2个回答

5

你说起你的起点是一个数组,但最高效的方法是使用Map而不是数组,其中键是名称,值是价格或包含价格的对象(取决于是否需要其他信息)。

使用未排序的数组

但如果你要使用数组进行操作,除非我们可以建立/维护排序后的数组(请参见下面的“使用排序后的数组”),否则没有比循环遍历它来查找带有给定name的前一个元素更有效率的方法。对此,filter不是正确的工具(您不需要它创建的数组)。你可以编写自己的循环:

let element;
for (let index = 0, length = array.length; index < length; ++index) {
    const thisElement = array[index];
    if (thisElement.name === name) {
        // Already have one
        element = thisElement;
        break;
    }
}
if (element) {
    element.price += price;
} else {
    array.push({name, price});
}

有些JavaScript引擎,如果你在循环之前声明了indexlengththisElement,可能会获得更多的速度提升,哪怕只是那么一点点。

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) {
            // Found! Update it
            element.price += price;
            return;
        }
        if (element.name < name) {
            left = guess + 1;
        } else {
            right = guess - 1;
        }
    }
    // Not found, insert it
    array.splice(left, 0, {name, price});
};

感谢您的输入。不幸的是,我必须使用数组,因为它是第三方模块实现的一部分,我无法更改它。谢谢。 - BitQueen
@BitQueen - 实际上,我做了一个不应该做的假设:我们能否对数组进行排序/按排序顺序构建数组?如果可以,我们可以进行二分查找,这将提供亚线性的查找时间。 - T.J. Crowder
1
它用于处理项的队列,以它们添加到数组的方式进行处理。 - BitQueen

1

针对一个名称,请使用find:

let array = [
  {name:"abc",  price: 24},
  {name:"cde",  price: 25},
  {name:"fgh",  price: 22},
  {name:"gfds", price: 21},
]

const elem = array.find(({name}) => name==='fgh');
if (elem) elem.price = "40";
console.log(array);


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接