繁体   English   中英

如何递归地在嵌套对象中查找值的键

[英]How to find the key of a value in a nested object recursively

我想在带有递归的 Javascript 嵌套对象中找到值的键。

这是我对该功能的尝试。 有没有更优雅的方法来实现这一点?

 const foo = { data: { data2: { data3: 'worked' }, data21: 'rand' }, data01: 'rand01' } function findKey(obj, target) { let result = null; if (_.isEmpty(obj) || !_.isObject(obj)){ return null; } if (!_.isArray(obj) && Object.keys(obj).length > 0) { for(let i=0; i < Object.keys(obj).length; i++){ let key = Object.keys(obj)[i]; let val = obj[key]; if (val === target) { return key; }else{ result = findKey(val, target); } if (result) {break} } } return result; } console.log(findKey(foo, 'worked'))
 <script src="//cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.min.js"></script>

例如,有没有办法避免必须检查result的值然后中断? 我觉得 result 应该能够在调用堆栈中冒泡,直到它在第一个函数调用时返回而不必中断。

您可以使用Object.entries来迭代所有键。

同样值得注意的是, Object.entries也适用于数组,因此不需要特殊处理。

 const foo = { data: { data2: { data3: 'worked' }, data21: 'rand' }, data01: 'rand01', arr: [{arrtest: "arr"},'xyz']} function findKey(obj, target) { const fnd = obj => { for (const [k, v] of Object.entries(obj)) { if (v === target) return k; if (typeof v === 'object') { const f = fnd(v); if (f) return f; } } } return fnd(obj); } console.log(findKey(foo, 'worked')) console.log(findKey(foo, 'arr')) console.log(findKey(foo, 'xyz'))

如果obj正是一个带有没有数组的子对象的普通对象,那么这就是诀窍。

 function findKey(obj, target) { for (let key in obj) { const val = obj[key]; if (val === target) { return key; } if (typeof val === "object" && !Array.isArray(val)) { const ret = findKey(val, target); if (ret) return ret; } } } const foo = { data: { data2: { data3: "worked" }, data21: "rand" }, data01: "rand01", }; console.log(findKey(foo, "worked")); console.log(findKey(foo, "bloop"));

在上面提出的几个问题之后,该函数看起来应该:

  • 假设输入总是一个对象。
  • 假设它可能会遇到数组。
  • 假设它必须在遇到一个值后停止(如果存在多个值)。

OP 提供的输入代码不处理数组情况。

下面的代码被采样以处理这些示例案例:

  • 普通嵌套对象结构。
  • 具有嵌套对象或元素数组的对象。

下面的函数接受第二个参数,它是一个回调,用于评估遇到的元素是否实际上是我们正在寻找的元素。 通过这种方式,可以更轻松地处理更复杂的检查。

递归方法被保留,一旦满足键,函数就会简单地return以避免不必要的搜索。

 const foo = { data: { data2: { data3: 'worked' }, data21: 'rand' }, data01: 'rand01' }; const fooWithArrays = { data: { data2: { data3: 'not here' }, data4: [ { data5: 'worked' }, { data6: 'not me' } ] } }; const fooWithExpression = { data: { data2: { data3: { id: 15, name: 'find me!' } }, data21: { data25: 'not me' } } }; const findKeyByValue = (obj, equalsExpression) => { // Loop key->value pairs of the input object. for (var [key, v] of Object.entries(obj)) { // if the value is an array.. if (Array.isArray(v)) { // Loop the array. for (let i = 0; i < v.length; i++) { // check whether the recursive call returns a result for the nested element. let res = findKeyByValue(v[i], equalsExpression); // if so, the key was returned. Simply return. if (res !== null && res !== undefined) return res; } } // otherwise.. else { // if the value is not null and not undefined. if (v !== null && v !== undefined) { // if the value is an object (typeof(null) would give object, hence the above if). if (typeof(v) === 'object') { // check whether the value searched is an object and the match is met. if (equalsExpression(v)) return key; // if not, recursively keep searching in the object. let res = findKeyByValue(v, equalsExpression); // if the key is found, return it. if (res !== null && res !== undefined) return res; } else { // finally, value must be a primitive or something similar. Compare. let res = equalsExpression(v); // if the condition is met, return the key. if (res) return key; // else.. continue. } } else continue; } } } console.log( findKeyByValue(foo, (found) => found === 'worked') ); console.log( findKeyByValue(fooWithArrays, (found) => found === 'worked') ); console.log( findKeyByValue(fooWithExpression, (found) => found && found.id && found.id === 15) );

这是最近重新提出的,但没有提到一种有用的技术,即生成器函数。 通常它们会简化需要提前停止的递归遍历。 在这里,我们将问题分解为两个函数。 一,生成器函数nestedEntries获取对象中的所有(嵌套)键值对。 另一个调用它并返回与提供的目标值匹配的第一个。

 function * nestedEntries (obj) { for (let [k, v] of Object .entries (obj)) { yield [k, v] if (Object (v) === v) {yield * nestedEntries (v)} } } const findKey = (obj, target) => { for (let [k, v] of nestedEntries (obj)) { if (v === target) return k } return null } const foo = {data01: 'rand01', data: {data21: 'rand', data2: { data3: 'worked' } }} console .log (findKey (foo, 'worked'))

您可以尝试正则表达式,如果数据只是没有数组的对象:

 const foo = { data: { data2: { data3: 'worked' }, data21: 'rand' }, data01: 'rand01' } const out = JSON.stringify(foo).match(/"([^{}]+)":"worked"/)[1]; console.log(out);

对于像这样的简单数据处理任务,我们使用object-scan 一旦你把头环绕在它周围,它就会非常强大,让事情变得更干净。 这是您解决问题的方法

(冒昧地从@briosheje 答案中获取输入数据)

 // const objectScan = require('object-scan'); const findKeyByValue = (data, fn) => objectScan(['**'], { abort: true, rtn: 'property', filterFn: ({ value }) => fn(value) === true })(data); const foo = { data: { data2: { data3: 'worked' }, data21: 'rand' }, data01: 'rand01' }; const fooWithArrays = { data: { data2: { data3: 'not here' }, data4: [{ data5: 'worked' }, { data6: 'not me' }] } }; const fooWithExpression = { data: { data2: { data3: { id: 15, name: 'find me!' } }, data21: { data25: 'not me' } } }; console.log(findKeyByValue(foo, (found) => found === 'worked')); // => data3 console.log(findKeyByValue(fooWithArrays, (found) => found === 'worked')); // => data5 console.log(findKeyByValue(fooWithExpression, (found) => found && found.id && found.id === 15)); // => data3 console.log(findKeyByValue(fooWithExpression, (found) => false)); // => undefined
 .as-console-wrapper {max-height: 100% !important; top: 0}
 <script src="https://bundle.run/object-scan@13.8.0"></script>

免责声明:我是对象扫描的作者

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM