按属性名称

时间:2016-11-15 07:03:00

标签: javascript recursion

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

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

13 个答案:

答案 0 :(得分:17)

您可以使用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'));

答案 1 :(得分:4)

您的代码有一些错误

  • 您递归调用util.findVal但未返回通话结果。代码应为return util.findVal(...)

  • 您未将属性名称key传递给递归调用

  • 您没有处理参考循环的可能性

  • 如果一个对象包含一个键,并且一个子对象包含返回值的键是随机的(取决于分析键的顺序)

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

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

如果你只是继续在obj1obj2中进行递归搜索,可能会导致无限递归。

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

  

ES6增加了使用SetMap检查对象标识的可能性,其中对象可用作键。这允许更快(亚线性)搜索时间。

以深度顺序运行的搜索解决方案可以是例如

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}}键时{{1}两个值处于相同的深度。)

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

答案 2 :(得分:2)

尝试更改像这样的其他语句

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

答案 3 :(得分:1)

如果找不到钥匙,请考虑一下。

我认为你可以做这样的事情而不是搜索

return object[propName] || null 

在你的代码中缺少一个断点,我猜你试图在整个对象内搜索而不仅仅是直接相关的属性,所以这里是你编辑的代码

修改

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

答案 4 :(得分:1)

我想你是说你想在属性和子属性的对象树中递归地查找属性名称。如果是这样,我将采用以下方式:

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

答案 5 :(得分:1)

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

使用类似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    

答案 6 :(得分:0)

答案取决于你想要的复杂程度。例如,JSON解析数组不包含函数 - 我相当肯定它不会包含设置为对象树中父节点的属性值。

此版本返回搜索对象树时找到的第一个属性名称的属性值。如果未找到命名属性或值为undefined,则返回undefined。需要进行一些修改来区分。它不会重新搜索已经搜索过的父节点,也不会尝试扫描空对象!

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
}

答案 7 :(得分:0)

我知道这是一篇很老的帖子,但我发现通过递归找到一个值来解决我遇到的问题很有帮助。我进一步提出了Nina Scholz给出的答案,并得出以下结论。它应该更快,因为每次递归调用时都不会创建所有键的数组。此外,如果找不到密钥,这将显式返回false。

&#13;
&#13;
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'));
&#13;
&#13;
&#13;

答案 8 :(得分:0)

旧问题,但要检查对象层次结构中属性是否存在,请尝试使用此简单选项

&#13;
&#13;
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" ) );
&#13;
&#13;
&#13;

答案 9 :(得分:0)

我最后写了这个函数。 它是这里找到的函数的重构:Recursively looping through an object to build a property list

添加了一个深度参数,以避免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);
            }*/
        }
    }
}

答案 10 :(得分:0)

在需要一个通用解决方案的领域中发现了这个问题,即检查对象是否在其层次结构中的任何位置(无论键如何)都包含特定值,而该值当然可以包括数组。因此,以下内容不能直接回答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 && typeof val === 'array') {
            return val.some((obj) => {
                return hasValue(obj, value);
            })
        }
    });
}

当然是受到@Nina Scholz解决方案的启发!

答案 11 :(得分:0)

返回具有指定名称的字段的

data是根节点/对象。 keyName是字段/成员的字符串名称。

如果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
    }
  }
}

for循环遍历每个字段,如果该字段是一个对象,则它将递归到该对象中。

答案 12 :(得分:0)

这是一段代码,可以在rootObj树中找到您要寻找的key。并将其添加到根对象。因此,到最后,您将可以访问像rootObj[key]这样的密钥。

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