正在使用多个';。过滤器';对大数组的调用对Javascript的性能有害?

正在使用多个';。过滤器';对大数组的调用对Javascript的性能有害?,javascript,arrays,Javascript,Arrays,我写这段代码是为了过滤一组单词。我为要过滤掉的每种类型的单词编写了一个过滤函数,并将它们按顺序应用于数组: const wordArray = rawArray.filter(removeNonDomainWords) .filter(removeWordsWithDigits) .filter(removeWordsWithInsideNonWordChars)

我写这段代码是为了过滤一组单词。我为要过滤掉的每种类型的单词编写了一个过滤函数,并将它们按顺序应用于数组:

  const wordArray = rawArray.filter(removeNonDomainWords)
                            .filter(removeWordsWithDigits)
                            .filter(removeWordsWithInsideNonWordChars)
                            .filter(removeEmptyWords)
                            .filter(removeSearchTerm, term)
                            .map(word => replaceNonWordCharsFromStartAndEnd(word))
如果我没有弄错的话,这段代码将在整个数组上迭代六次

编写一个(更复杂,但在我的场景中仍然很简单)过滤函数,将过滤函数逻辑地组合起来以获得相同的结果,不是更有效吗?

我在函数式编程的环境中学习了过滤器,它可以使我的代码更短更快。这就是为什么我可能没有质疑我在写什么,认为“我在做FP,这一定很好”

谢谢

它确实迭代了六次,但不一定在整个初始数组上迭代。每过滤一次,它就会变小。使用一种过滤方法会更有效,但差异可能不像您预期的那么大

如果仍要使用此解决方案,可以首先使用最具选择性的过滤器(即期望过滤掉最多的过滤器)来提高性能。这样,下面的数组将更小,迭代次数也会更少

正如@Redu(在评论中)指出的,您可以使用
| |
操作符链接过滤器。这将确保您只进行一次迭代


这背后的原因是
Array.prototype.filter
返回一个新数组。将其与Java
Stream
API进行比较,后者返回一个流,因此可以通过调用列表“深度优先”。缺点是最终需要一个终端操作来“收集”结果

在javascript中

rawArray.filter(x)
迭代
rawArray
并返回一个新的过滤数组-该数组可以依次过滤或按原样使用。它将导致对
rawArray
中的每个元素调用
x

在Java中,等价物是

rawArray.stream().filter(x)
实际上在这一点上什么都做不了。不会调用
x
。返回值将是一个
,可以稍后使用。可以对其进行进一步过滤,但直到通过终端操作以某种方式收集这些值之后,才会进行调用

让我们比较一下javascript

rawArray.filter(x).filter(y).length
到爪哇

rawArray.stream().filter(x).filter(y).count()
在javascript中,这将首先迭代
rawArray
的所有元素,为每个元素调用
x
,并将结果存储在中间数组中。然后javascript引擎将迭代中间数组的所有元素,为每个元素调用
y
,并将结果存储在第二个中间数组中,然后检查数组的大小

在Java中,代码片段将导致VM在
rawArray
的元素上迭代,首先调用
x
,如果
x
true
,则对每个元素调用
y
,如果仍然
true
,则递增计数器。不会有中间数组,数据集上只有一次迭代


函数式编程很有趣,如果使用得当,它会创建更少的代码,这些代码不那么复杂,理想情况下甚至更易于阅读,但它确实将许多责任移交给了框架(或引擎、VM或其他什么),重要的是要认识到看似相似的代码,同时表现出相似的行为,可以在不同的环境中执行截然不同的操作。

正如其他人所回答的那样:您的链将在每个
调用的(过滤)数据上循环。filter
调用可能会影响性能,但可能不会,除非您修改/过滤数千个字符串

如果您不想在性能和可读性上妥协,您可以创建自己的过滤器包装器,它支持链接和延迟计算

下面的示例显示了一个包装器,它“记住”您传递给它的筛选方法,但只在您说完成后调用它们

const ArrayFilter=(arr,pred)=>({
过滤器:pred
?(newPred)=>ArrayFilter(arr,x=>pred(x)和&newPred(x))
:(newPred)=>阵列过滤器(arr,newPred),
执行:(映射器)=>映射器
?阵列滤波器(pred)
:arr.filter(pred.map)(映射器)
});
//一些测试数据
const oneToFifty=Array.from(Array(50),(i,i)=>i);
//ArrayFilter允许链式语法,但只允许在数据上循环
//仅在需要时运行一次过滤器
console.log(
阵列过滤器(1/4)
.filter(x=>x%3==0)
.filter(x=>x<25)
.filter(x=>x>10)
.execute(n=>`Result:${n}`)

);
您可以使用
|
操作符将所有
removeXX
函数链接到一个过滤器中。是的,这正是我的想法。但它会提高性能吗?如果是这样,为什么?这是关于单次迭代和单次结果数组创建的每个项目的多次检查的成本,而不是多次迭代和多次结果数组创建的每个项目的单次检查的成本。我将赌注押在第一个上,因为
|
操作符在满足
true
值时不会调用剩余的检查,这将节省大量冗余函数调用。我明白了,因此迭代本身并不昂贵,只是不必要的检查,如果使用了
|
运算符。我猜是一次迭代解决方案?!(感觉好像一个学生不确定老师想听到什么…))我理解你的意思,这绝对是一个需要考虑的问题。但在我的例子中,过滤不会影响数组的大小。我想知道的是迭代本身是否需要很多时间,如果可能的话应该避免。@Flip如果你不希望缩小数组,过滤的意义是什么?它正在缩小,但是如果过滤的单词只占数组的1-5%