简体   繁体   English

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

[英]Search recursively for value in object by property name

I'm building an utility function that should search for a property name and return its value once it is found.我正在构建一个实用程序 function 应该搜索属性名称并在找到后返回其值。 It should do this recursively:它应该递归地执行此操作:

// 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')

However the console log goes forever and the browser crashes.然而,控制台日志永远消失,浏览器崩溃。 What am I doing wrong?我究竟做错了什么?

EDIT:编辑:

This is how I'm calling the function:这就是我调用 function 的方式:

// Input

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

this.findProgress(item)

methods: {
  findProgress (item) {
    return util.findVal(item, this.propName)
  }
}

Your code has a few errors:您的代码有一些错误:

  • You're recursively calling util.findVal but not returning the result of the call.您递归调用util.findVal但不返回调用结果。 Code should be return util.findVal(...)代码应该是return util.findVal(...)
  • You're not passing the attribute name key to the recursive call您没有将属性名称key传递给递归调用
  • You're not handling the possibility of a reference loop您没有处理参考循环的可能性
  • If an object contains a key and also a sub-object that contains the key which value is returned is random (depends on the sequence in which the keys are analyzed)如果一个对象包含一个键以及一个包含键的子对象,则返回的值是随机的(取决于分析键的顺序)

The third problem is what can cause infinite recursion, for example:第三个问题是什么会导致无限递归,例如:

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

if you just keep looking recursively searching in obj1 or obj2 could lead to infinite recursion.如果您只是继续在obj1obj2中递归搜索可能会导致无限递归。

Unfortunately for reasons not clear to me in Javascript is impossible to know the object "identity"... (what Python id(x) does) you can only compare an object to another. 不幸的是,由于我在 Javascript 中不清楚的原因,不可能知道对象“身份”......(Python id(x)做了什么)你只能将一个对象与另一个对象进行比较。 This means that to know if an object has already been seen in the past you need a linear scan with known objects. 这意味着要知道过去是否已经看到过某个对象,您需要对已知对象进行线性扫描。

ES6 added the possibility to check object identity with Set and Map where objects can be used as keys. ES6 添加了使用SetMap检查对象身份的可能性,其中对象可以用作键。 This allows for faster (sub-linear) search times.这允许更快的(亚线性)搜索时间。

A search solution that runs in depth order could be for example:例如,按深度顺序运行的搜索解决方案可能是:

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;
}

given an object and an attribute name, returns all the values found with that name at the first depth they are found (there can be more than one value: for example when searching {x:{z:1}, y:{z:2}} for the key "z" two values are at the same depth).给定一个对象和一个属性名称,返回在找到它们的第一个深度找到的具有该名称的所有值(可以有多个值:例如在搜索{x:{z:1}, y:{z:2}}"z"的两个值处于相同的深度)。

The function also correctly handles self-referencing structures avoiding infinite search.该函数还正确处理避免无限搜索的自引用结构。

Don't write your own utility if you can avoid it.如果可以避免,请不要编写自己的实用程序。

Use something like jsonpath<\/a>使用类似jsonpath 的<\/a>东西

Some examples of supported syntax:支持的语法的一些示例:

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    

尝试像这样更改 else 语句

return util.findVal(object[key],propName)<\/code>

"

I know this is an old post, but I found it helpful to answer a problem I had with recursively finding a value by it's key.我知道这是一篇旧帖子,但我发现它有助于回答我在递归地通过它的键查找值时遇到的问题。 I further developed the answer given by Nina Scholz, and came up with the following.我进一步开发了 Nina Scholz 给出的答案,并提出了以下内容。 It should be quicker as it is not creating an array of all of the keys each time it is recursively invoked.它应该更快,因为它不会在每次递归调用时创建所有键的数组。 Also, this will explicitly return false if the key is not found.此外,如果找不到密钥,这将显式返回 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'));<\/code><\/pre>

"

Think about it if there is no key found.如果没有找到密钥,请考虑一下。

I think you could do something like this instead of search我认为你可以做这样的事情而不是搜索

return object[propName] || null 

In your code there was a breakpoint missing, I guess you are trying to search inside the whole object not just the directly related attributes so here is an edit for you code在您的代码中缺少断点,我想您正在尝试在整个对象中搜索,而不仅仅是直接相关的属性,所以这里是您的代码编辑

EDIT:编辑:

util.findVal = (object, propName) =>{
 if(!!object[propName]){
   return object[propName]
 }else{
   for (let key in object) {
     if(typeof object[key]=="object"){
      return util.findVal(object[key], propName)
     }else{
      return null
     }
  }
 }
}

I think you are saying that you want to look for the property name anywhere recursively within the objects tree of properties and sub-properties.我认为您是说您想在属性和子属性的对象树中递归地查找属性名称。 If so, here is how I would approach this:如果是这样,这就是我将如何处理这个问题:

var object1 = _getInstance(); // somehow we get an object
var pname = 'PropNameA';

var findPropertyAnywhere = function (obj, name) {
    var value = obj[name];
    if (typeof value != 'undefined') {
        return value;
    }
    foreach(var key in obj) {
        var v2 = findPropertyAnywhere(obj[key], name);
        if (typeof v2 != 'undefined') {
            return v2;
        }
    }
    return null;
}
findPropertyAnywhere(object1, pname);

Found this question in the realm of needing a general solution to check if an object contains a specific value anywhere in its hierarchy (regardless of the key), which can include arrays of course.在需要通用解决方案来检查对象是否在其层次结构中的任何位置包含特定值(无论键如何)的领域中发现了这个问题,其中当然可以包括数组。 So the following does not answer OPs question directly or improve upon other solutions but it might help others looking for the same thing I did and finding this post:因此,以下内容不会直接回答 OP 的问题或改进其他解决方案,但它可能会帮助其他人寻找我所做的相同事情并找到这篇文章:

function hasValue(object, value) {
    return Object.values(object).some(function(val) {
        if (val === value) {
            return true;
        }
        if (val && typeof val === 'object') {
            return hasValue(val, value);
        }
        if (val && val.isArray()) {
            return val.some((obj) => {
                return hasValue(obj, value);
            })
        }
    });
}

An answer depends a on how complex you want to get.答案取决于您想要获得的复杂程度。 For example a JSON parsed array doesn't contain functions - and I'm fairly certain it won't contain property value set to a parent node in object tree.例如,JSON 解析数组不包含函数 - 我相当确定它不会包含设置为对象树中父节点的属性值。

This version returns the property value of the first property name found whilst searching the object tree.此版本返回在搜索对象树时找到的第一个属性名称的属性值。 undefined<\/code> is returned if either the named property was not found or has a value of undefined<\/code> .如果未找到命名属性或具有undefined<\/code>值,则返回undefined<\/code> 。 Some modifications would be needed to tell the difference.需要进行一些修改才能区分。 It does not re-search parent nodes already being searched, nor try to scan null objects!它不会重新搜索已搜索的父节点,也不会尝试扫描空对象!

let util = {};

util.findVal = (object, propName, searched=[]) => {
  searched.push( object)
  for (let key in object) {
    if (key === propName) {
      return object[key]
    } 
    else {
      let obj = object[ key]
      if( obj && (typeof obj == "object" || typeof obj == "function")) {
          if( searched.indexOf(obj) >=0) {
              continue
          }
          let found = util.findVal(obj, propName, searched)
          if( found != searched) {
              return found
          }
      }
    }
  }
  searched.pop();
  // not in object:
  return searched.length ? searched : undefined
}

I ended up writing this function.我最终写了这个函数。 It is a refactor of a function found here: Recursively looping through an object to build a property list这是对此处找到的函数的重构: Recursively looping through an object to build a property list

added a depth parameter to avoid stack overflow in chrome devtools.添加了一个深度参数以避免 chrome devtools 中的堆栈溢出。

function iterate(obj, context, search, depth) {
    for (var property in obj) {
        if (Object.prototype.hasOwnProperty.call(obj, property)) {
            if(typeof obj[property] == 'function') continue;

            if( property == search ){
                console.log(context+property);
                return;
            }

            if (typeof obj[property] == "object" && depth < 7) {
                //console.log('--- going in: ' + context+property);
                iterate(obj[property], context+property+'.', search, depth+1);
            }
            /*else {
                console.log(context+property);
            }*/
        }
    }
}

Returns the value of the field with the specified name.返回具有指定名称的字段的值

data is the root node/object. data是根节点/对象。 keyName is a string name of the field/member. keyName是字段/成员的字符串名称。

If keyName specifies a field that is itself an object, then that object is returned.如果keyName指定的字段本身就是一个对象,则返回该对象。

function find (data, keyName) {
  for (const key in data) {

    const entry = data[key]
    if (key === keyName)
      return entry

    if (typeof entry === 'object') {
      const found = find(entry, keyName)

      if (found)
        return found
    }
  }
}

The for loop goes through each field and if that field is an object then it will recurse into that object. for循环遍历每个字段,如果该字段是一个对象,那么它将递归到该对象。

Here is a piece of code which find the key<\/code> you are looking for in your rootObj tree.这是一段代码,可在 rootObj 树中找到您要查找的key<\/code> 。 And add it to the root object.并将其添加到根对象。 So by the end you will have access to you key like this rootObj[key]<\/code> .所以到最后你将可以访问你的密钥,比如rootObj[key]<\/code> 。

findKeyVal(object, key, rootObj) {
      if(object instanceof Object) {
        let keys = Object.keys(object);
        if(keys.includes(key) && !isNullOrUndefined(object[key])) {
          rootObj[key] = object[key];
          return;
        }
        else {
          keys.filter(k => object[k] instanceof Object).forEach( k => {
            this.findKeyVal(object[k], key, rootObj);
          })
        }
      }
    }

Old question, but to check if the property exists anywhere in the hierarchy of an object, try this simple option老问题,但要检查属性是否存在于对象层次结构中的任何位置,请尝试这个简单的选项

 var obj = { firstOperand: { firstOperand: { firstOperand: { sweptArea: 5 } } } }; function doesPropertyExists ( inputObj, prop ) { return JSON.stringify(obj).indexOf( "\\""+ prop +"\\":" ) != -1; }; console.log( doesPropertyExists( obj, "sweptArea" ) ); console.log( doesPropertyExists( obj, "firstOperand" ) ); console.log( doesPropertyExists( obj, "firstOperand22" ) );

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

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