Array.prototype.map 和 Array.prototype.find
您似乎对实现细节很固执,但我敢肯定您不知道为什么。 for 或 while 或 filter 没有任何问题。在这篇文章的结尾,我不希望你相信我,而是希望你知道这是真的。
您可以使用 map 和 find 简单地解决这个问题 - 对于所有意图和目的,如何 map 和 find 实现对您来说是个谜。他们是否使用for 循环甚至GOTO 来大声喊叫都没关系。对你来说重要的是你能以一种实用的方式表达你的问题。
map 和 find 是谦逊的 JavaScript 函数式程序员的完美伴侣...
var d = [{'name':'a', 'value':1}, {'name':'b', 'value':2}, {'name':'c', 'value':3}, {'name':'d', 'value':4}, {'name':'e', 'value':5}, {'name':'f', 'value':6}]
var a = ['a','e']
var result = a.map(x => d.find(y => y.name === x))
console.log(result)
// [ { name: 'a', value: 1 },
// { name: 'e', value: 5 } ]
泛型函数促进代码重用
使用一些泛型函数,我们可以用更可重用的方式表达解决方案
var data = [{'name':'a', 'value':1}, {'name':'b', 'value':2}, {'name':'c', 'value':3}, {'name':'d', 'value':4}, {'name':'e', 'value':5}, {'name':'f', 'value':6}]
const findBy = k => xs => v =>
xs.find(x => x[k] === v)
const findAllBy = k => xs => vs =>
vs.map(findBy(k)(xs))
console.log(findAllBy ('name') (data) (['a', 'e']))
// [ { name: 'a', value: 1 },
// { name: 'e', value: 5 } ]
为什么固执无济于事
如果您想看看这是如何在不依赖任何的情况下完成的,for、while、filter、map 或 find...
var data = [{'name':'a', 'value':1}, {'name':'b', 'value':2}, {'name':'c', 'value':3}, {'name':'d', 'value':4}, {'name':'e', 'value':5}, {'name':'f', 'value':6}]
const main = ([x,...xs]) => {
const aux = ([y,...ys]) => {
if (y === undefined)
return null
else if (y.name === x)
return y
else
return aux (ys)
}
if (x === undefined)
return []
else
return [aux(data), ...main(xs)]
}
console.log(main(['a', 'e']))
// [ { name: 'a', value: 1 },
// { name: 'e', value: 5 } ]
很痛苦吧?与这里的其他答案相比,该代码的执行情况更少,这些答案仍然具有 length、indexOf、slice 或整个 lodash 库等依赖项。
所以这样做真的没有意义。在这个示例中编码了大量有用的通用函数,我们希望在其他地方使用它们。这就是为什么像map、reduce、filter和find这样的函数首先存在的原因——for和while也是如此。
每次我想遍历一个数组,或者过滤一个数组,或者在一个数组中查找一个元素时,我都不想手动完成。我使用函数的原因是我不必重复自己……永远。
好奇心
所以也许你对上面的map+find 解决方案没问题,但你也很好奇如果你还没有它们,你将如何自己实现它们——下面是无数种实现这些功能的方法中只有 2 种。
var data = [{'name':'a', 'value':1}, {'name':'b', 'value':2}, {'name':'c', 'value':3}, {'name':'d', 'value':4}, {'name':'e', 'value':5}, {'name':'f', 'value':6}]
const find = f => ([x,...xs]) =>
x === undefined ? null :
f(x) === true ? x : find (f) (xs)
const map = f => ([x,...xs]) =>
x === undefined ? [] : [f(x), ...map(f)(xs)]
const main =
map (x => find (y => y.name === x) (data))
console.log(main(['a', 'e']))
// [ { name: 'a', value: 1 },
// { name: 'e', value: 5 } ]
递归在 JavaScript 中很糟糕
很多人不了解 JavaScript 中的递归,它实际上并不是很好。我们承诺在 ES6 中消除尾调用,但当前的 JavaScript 实现还没有真正支持它。这意味着您编写的任何递归函数(除非您将其蹦床)都存在堆栈溢出的潜在风险。
这意味着用for 和while 实现功能接口所以要好得多。
下面,我们重新实现了map 和find,但这次我们以一种巧妙的方式避免了堆栈溢出的风险。请注意,它对生成的代码并没有真正的影响。我们仍然可以与map 和find 以一种很好的、实用的方式进行交互——而且它们都是引用透明的
请特别注意main 函数——它与上面的示例完全没有变化。我们“丑陋的”while 和 for 循环作为实现细节被巧妙地隐藏起来了,最终用户一点也不聪明。
var data = [{'name':'a', 'value':1}, {'name':'b', 'value':2}, {'name':'c', 'value':3}, {'name':'d', 'value':4}, {'name':'e', 'value':5}, {'name':'f', 'value':6}]
const find = f => xs => {
for (let x of xs)
if (f(x) === true)
return x
return null
}
const map = f => xs => {
const acc = Array(xs.length)
for (let [i,x] of xs.entries())
acc[i] = f(x)
return acc
}
const main =
map (x => find (y => y.name === x) (data))
console.log(main(['a', 'e']))
// [ { name: 'a', value: 1 },
// { name: 'e', value: 5 } ]