按属性名称递归搜索对象中的值

IT技术 javascript recursion
2021-01-27 10:55:31

我正在构建一个实用程序函数,该函数应该搜索属性名称并在找到后返回其值。它应该递归地执行此操作:

// Function
util.findVal = (object, propName) => {
  for (let key in object) {
    if (key === propName) {
      console.log(propName)
      console.log(object[key])
      return object[key]
    } else {
      util.findVal(object[key], propName)
    }
  }
}

// Input
object: {
  photo: {
    progress: 20
  }
}

// Usage
util.findVal(object, 'progress')

然而,控制台日志永远存在并且浏览器崩溃。我究竟做错了什么?

编辑:

这就是我调用函数的方式:

// Input

item: {
  photo: {
    file: {},
    progress: 20
  }
}

this.findProgress(item)

methods: {
  findProgress (item) {
    return util.findVal(item, this.propName)
  }
}
6个回答

您可以使用Object.keys和迭代Array#some

function findVal(object, key) {
    var value;
    Object.keys(object).some(function(k) {
        if (k === key) {
            value = object[k];
            return true;
        }
        if (object[k] && typeof object[k] === 'object') {
            value = findVal(object[k], key);
            return value !== undefined;
        }
    });
    return value;
}

var object =  { photo: { progress: 20 }};
console.log(findVal(object, 'progress'));

@alex:这一切都归结为您对“作品”的定义。
2021-03-15 10:55:31
哈,在所有答案中,您的答案是唯一有效的。奇怪的。
2021-03-22 10:55:31
小心包含循环引用的对象。你会得到一个......等待......等待......它......Stack Overflow错误!上帝,我很有趣。
2021-03-25 10:55:31

您的代码有几个错误:

  • 您正在递归调用util.findVal但不返回调用结果。代码应该是return util.findVal(...)
  • 您没有将属性名称传递key给递归调用
  • 您没有处理参考循环的可能性
  • 如果一个对象包含一个键和一个包含键的子对象,则返回的值是随机的(取决于分析键的顺序)

第三个问题是什么会导致无限递归,例如:

var obj1 = {}, obj2 = {};
obj1.x = obj2; obj2.y = obj1;

如果您只是继续递归搜索obj1obj2可能导致无限递归。

不幸的是,由于我在 Javascript 中不清楚的原因,不可能知道对象“身份”......(Pythonid(x)什么)你只能将一个对象与另一个对象进行比较。这意味着要知道过去是否已经看到某个对象,您需要对已知对象进行线性扫描。

ES6 增加了检查对象身份的可能性,Set以及Map对象可以用作键的位置。这允许更快的(亚线性)搜索时间。

例如,按深度顺序运行的搜索解决方案可能是:

function findVal(obj, key) {
    var seen = new Set, active = [obj];
    while (active.length) {
        var new_active = [], found = [];
        for (var i=0; i<active.length; i++) {
            Object.keys(active[i]).forEach(function(k){
                var x = active[i][k];
                if (k === key) {
                    found.push(x);
                } else if (x && typeof x === "object" &&
                           !seen.has(x)) {
                    seen.add(x);
                    new_active.push(x);
                }
            });
        }
        if (found.length) return found;
        active = new_active;
    }
    return null;
}

给定一个对象和一个属性名称,返回在它们找到的第一个深度处用该名称找到的所有值(可以有多个值:例如,当搜索两个值处于相同深度{x:{z:1}, y:{z:2}}的键"z")。

该函数还可以正确处理自引用结构,避免无限搜索。

在尝试编辑此答案以修复示例的“else if”上缺少的右括号时,Stack Overflow 对最小更改字符数量的限制被强制执行一些不相关的更改以提交编辑以供审核。我希望它不会因此被拒绝。
2021-03-15 10:55:31

如果可以避免,请不要编写自己的实用程序。

使用类似jsonpath 的东西

支持的语法的一些示例:

JSONPath                   Description
$.store.book[*].author      The authors of all books in the store
$..author                   All authors
$.store.*                   All things in store, which are some books and a red bicycle
$.store..price              The price of everything in the store
$..book[2]                  The third book
$..book[(@.length-1)]       The last book via script subscript
$..book[-1:]                The last book via slice
$..book[0,1]                The first two books via subscript union
$..book[:2]             The first two books via subscript array slice
$..book[?(@.isbn)]          Filter all books with isbn number    
这是个好主意,但没有回答问题
2021-04-09 10:55:31

尝试像这样改变 else 语句

return util.findVal(object[key],propName)

我知道这是一篇旧帖子,但我发现它有助于回答我在递归查找值时遇到的问题。我进一步完善了 Nina Scholz 给出的答案,并提出了以下内容。它应该更快,因为它不会在每次递归调用时创建所有键的数组。此外,如果未找到密钥,这将显式返回 false。

function findVal(obj, keyToFind) {
  if (obj[keyToFind]) return obj[keyToFind];

  for (let key in obj) {
    if (typeof obj[key] === 'object') {
      const value = findVal(obj[key], keyToFind);
      if (value) return value;
    }
  }
  return false;
}

var object =  { photo: { progress: 20 }};
console.log(findVal(object, 'progress'));