我一直在移动我的一些数字生活,我不得不做的一件事是从谷歌照片中下载我所有的照片。由于这些照片的组织方式,我发现需要重新安排它们,所以我写了一个小节点脚本来做这件事。这个脚本的作用与这篇文章不完全相关,所以我不打算详述,(但如果你想通读的话,这里有整个脚本)。下面是我想说的部分(为清晰起见略作编辑)。
const lines = execSync(`find "${searchPath}" -type f`).toString().split('\n')
const commands = lines
.map(f => f.trim())
.filter(Boolean)
.map(file => {
const destFile = getDestFile(file)
const destFileDir = path.dirname(destFile)
return `mkdir -p "${destFileDir}" && mv "${file}" "${destFile}"`
})
commands.forEach(command => execSync(command))
基本上,它所做的是使用linuxfind 命令来查找一个目录中的文件列表,然后将该脚本的结果分成几行,修剪它们以去除空白,删除空行,然后将它们映射到移动这些文件的命令,然后运行这些命令。
我在twitter上分享了这个脚本,有几个人批评了这个脚本,建议我可以用reduce。我很确定他们两个人都建议我把它作为一种性能优化,因为你可以减少(不是双关语)JavaScript在数组上的循环次数。
现在,要明确的是,这个数组中大约有5万个项目,所以肯定比你在典型的UI开发中处理的几十个项目要多,但我想首先说明一点,在一次性脚本这种情况下,你运行一次就可以了,性能基本上应该是最后要担心的事情(除非你所做的事情真的非常昂贵)。在我的例子中,它运行得非常快。慢的部分不是多次迭代数组中的元素,而是运行这些命令。
还有一些人建议我使用Node APIs,甚至是npm的开源模块来帮助运行这些脚本,因为它 "可能会更快,而且可以跨平台运行"。同样,他们可能没有错,但对于 "足够快 "的一次性脚本,这些东西并不重要。这是一个典型的例子,在一个问题上应用不相关的约束条件,导致了一个更复杂的解决方案。
在任何情况下,我确实想解决使用reduce ,而不是我在那里的map ,filter ,然后map 的想法。
用reduce
下面是同样的代码,如果我们使用reduce
const commands = lines.reduce((accumulator, line) => {
let file = line.trim()
if (file) {
const destFile = getDestFile(file)
const destFileDir = path.dirname(destFile)
accumulator.push(`mkdir -p "${destFileDir}" && mv "${file}" "${destFile}"`)
}
return accumulator
}, [])
现在,我不是那些认为reduce 是邪恶的卵子的人之一(查看那个主题中有趣的reduce的例子),但我确实觉得我可以识别代码实际上更简单/更复杂,我说这里的reduce例子肯定比链式例子更复杂。
与loop
说实话,我使用数组方法已经很久了,我需要一秒钟的时间来把这个改写成for循环。所以...一秒钟...
好了,给你。
const commands = []
for (let index = 0; index < lines.length; index++) {
const line = lines[index]
const file = line.trim()
if (file) {
const destFile = getDestFile(file)
const destFileDir = path.dirname(destFile)
commands.push(`mkdir -p "${destFileDir}" && mv "${file}" "${destFile}"`)
}
}
这也没有多简单。
**edit:**but wait!我们可以简化这个问题,多亏了for..of!
const commands = []
for (const line of lines) {
const file = line.trim()
if (!file) continue
const destFile = getDestFile(file)
const destFileDir = path.dirname(destFile)
commands.push(`mkdir -p "${destFileDir}" && mv "${file}" "${destFile}"`)
}
老实说,我确实认为这并不比传统的循环好多少,但我确实认为这很简单。我认为有些人因为for循环是 "必须的 "而对其大打折扣,而实际上它们是非常有用的。
我的看法
通常情况下,我将会在链式循环和for..of 循环之间做出选择。如果我对在数组上多次迭代有性能上的担忧,那么for..of 肯定会成为我的选择。
我不经常使用reduce ,但有时我会尝试一下,将它与其他选项进行比较,然后选择它。我知道这听起来很主观,但很多编码都是主观的,所以🤷♂️
我很想听听你的看法。请回复下面的推文,让我知道。如果你喜欢的话,也可以随时转发。