在使用loDash的过滤器中查找数组中的部分字符串的最快方法是什么?

2022-08-05 00:00:00 node.js filter reduce javascript lodash

有没有比下面的方法更快地在数组中查找部分字符串的方法?

以下是我的示例数据:

const products = [
  {
    "product": "test1",
    "price": 11
  },
  {
    "product": "test2",
    "price": 31
  },
  {
    "product": "xxxx",
    "price": 21
  },
  {
    "product": "ssss",
    "price": 22
  },
]

以下是我的关键字(但实际上,还有很多):

const keywords = [ "test", "xx" ]

我想用关键字筛选产品,然后将所有产品价格相加,我的输出应该是

63

我试着这样做:我先过滤我的产品,我使用indexOf是因为它比includes快:

const fil = _.filter(products, (product) => {
    return _.some(keywords, (v) => product.name.indexOf(v) >= 0 );
});

然后我用reduce对它们求和:

const sum = fil.reduce(function (sum, data) {
    return sum + data.price;
}, 0);
一切都运行得很好,但如果我必须处理大约300k个元素和100k个关键字,大约需要3分钟才能找到这个查询。有什么办法可以缩短这段时间吗?(product值非常唯一,没有太多重复项。)


解决方案

您说过您只需要总和,而不是筛选出的产品列表,因此我们可以将该时间略微减少,但可能不会大幅减少:

  1. 通过不生成我们不需要的数组来减少内存颠覆量
  2. 当我们只需要一次时,不要进行两次通过products
  3. 避免函数调用(尽管函数调用在JavaScript引擎中)

所以:

let sum = 0;
for (const {product, price} of products) {
    for (const keyword of keywords) {
        if (product.includes(keyword)) {
            sum += price;
            break;
        }
    }
}

使用新的(Ish)for-of循环。然而,在幕后,for-of涉及函数调用和创建对象(尽管当循环遍历本机数组时,JavaScript引擎可能能够完全消除这些优化)。您可能想在一个典型的for循环中尝试一下,看看哪个循环的速度最快:

let sum = 0;
let productsLength = products.length;
let keywordsLength = keywords.length;
let pIndex, kIndex;
for (pIndex = 0; pIndex < productsLength; ++pIndex) {
    const {product, price} = products[pIndex];
    for (kIndex = 0; kIndex < keywordsLength; ++kIndex) {
        if (product.includes(keywords[kIndex])) {
            sum += price;
            break;
        }
    }
}

类似地,将循环变量放在for (...)部分内与将它们放在for (...)部分外进行试验,但我倾向于认为在外部只会稍微快一点(因为letfor循环中的工作方式)。

最后,您可以将其与使用本机(而不是lowash)forEachsome方法而不是for循环进行比较。

相关文章