如果您真的不关心匹配哪个节点/值,请使用@trincot 的解决方案。它直截了当,写得很好,并且非常有效地解决了您的问题。
如果您想要的不仅仅是一个布尔值作为您的挖掘结果,请继续阅读...
我真的怀疑你是否需要这个,但如果你的对象非常大,你会想要一个提前退出行为——这意味着一旦找到匹配项,迭代你的输入数据将停止,true/false 结果将立即返回。 @trincot 的解决方案提供提前退出,但使用 map、filter 或 reduce 的解决方案不提供此类行为。
findDeep 比仅仅检查一个字符串值是否以另一个字符串值开头更有用——它需要一个应用于数据中每个叶节点的高阶函数。
这个答案使用我的findDeep 过程来定义一个通用的anyStartsWith 过程,方法是检查findDeep 是否返回undefined(不匹配)
它适用于任何输入类型,它会遍历Object 和Array 子节点。
const isObject = x=> Object(x) === x
const isArray = Array.isArray
const keys = Object.keys
const rest = ([x,...xs]) => xs
const findDeep = f => x => {
let make = (x,ks)=> ({node: x, keys: ks || keys(x)})
let processNode = (parents, path, {node, keys:[k,...ks]})=> {
if (k === undefined)
return loop(parents, rest(path))
else if (isArray(node[k]) || isObject(node[k]))
return loop([make(node[k]), make(node, ks), ...parents], [k, ...path])
else if (f(node[k], k))
return {parents, path: [k,...path], node}
else
return loop([{node, keys: ks}, ...parents], path)
}
let loop = ([node,...parents], path) => {
if (node === undefined)
return undefined
else
return processNode(parents, path, node)
}
return loop([make(x)], [])
}
const startsWith = x => y => y.indexOf(x) === 0
const anyStartsWith = x => xs => findDeep (startsWith(x)) (xs) !== undefined
let obj = {
'child' : {
'child_key': 'asdfghhj'
},
'free': 'notasd',
'with': 'asdhaheg'
}
console.log(anyStartsWith ('asd') (obj)) // true
console.log(anyStartsWith ('candy') (obj)) // false
你会发现这有点浪费findDeep 的潜力,但如果你不需要它的力量,那么它不适合你。
这是findDeep的真正力量
findDeep (startsWith('asd')) (obj)
// =>
{
parents: [
{
node: {
child: {
child_key: 'asdfghhj'
},
free: 'notasd',
with: 'asdhaheg'
},
keys: [ 'free', 'with' ]
}
],
path: [ 'child_key', 'child' ],
node: {
child_key: 'asdfghhj'
}
}
生成的对象有 3 个属性
-
parents – 对匹配值沿袭中每个节点的完整对象引用
-
path – 获取匹配值的键路径(堆栈反转)
-
node – 匹配的键/值对
你可以看到,如果我们把父对象作为p,并把路径栈倒过来,我们就得到了匹配的值
p['child']['child_key']; //=> 'asdfghhj'