Javascript对象递归以查找最深层次的项目

时间:2017-03-12 22:35:27

标签: javascript object search recursion

我试图以递归方式搜索包含字符串,数组和其他对象的对象,以便在最深层找到一个项目(匹配一个值)但是我总是将未定义作为返回结果。我可以通过一些控制台日志记录看到我找到了该项目,但它被覆盖了。我知道哪里出错了?

  var theCobWeb = {
    biggestWeb: {
      item: "comb",
      biggerWeb: {
        items: ["glasses", "paperclip", "bubblegum"],
        smallerWeb: {
          item: "toothbrush",
          tinyWeb: {
            items: ["toenails", "lint", "wrapper", "homework"]
          }
        }
      },
      otherBigWeb: {
        item: "headphones"
      }
    }
  };

  function findItem (item, obj) {
  var foundItem;
  for (var key in obj) {
    if (obj[key] === item) {
      foundItem = obj;
    } else if (Array.isArray(obj[key]) && obj[key].includes(item)) {
      foundItem = obj;
    } else if (typeof obj[key] === 'object' && !Array.isArray(obj[key])) {
      findItem(item, obj[key]);
    } 
  }
      return foundItem;
}

var foundIt = findItem('glasses', theCobWeb);
console.log('The item is here: ' + foundIt); // The item is here: undefined 

编辑:根据以下反馈清理一下代码。

3 个答案:

答案 0 :(得分:1)

function findItem (item, obj) {
  for (var key in obj) {
    if (obj[key] === item) { // if the item is a property of the object
      return obj;            // return the object and stop further searching
    } else if (Array.isArray(obj[key]) && obj[key].includes(item)) { // if the item is inside an array property of the object
      return obj;            // return the object and stop the search
    } else if (typeof obj[key] === 'object' && !Array.isArray(obj[key])) { // if the property is another object
      var res = findItem(item, obj[key]); // get the result of the search in that sub object
      if(res) return res; // return the result if the search was successful, otherwise don't return and move on to the next property
    } 
  }
  return null; // return null or any default value you want if the search is unsuccessful (must be falsy to work)
}

注1: Array.isArrayArray.prototype.includes已经返回布尔值,因此无需针对布尔值进行检查。

注2:您可以使用 NOT运算符!)来翻转布尔值。

注3:您必须在找到结果后立即返回结果(如果找到),这样您就不会浪费时间寻找已有的结果。

Note4:搜索的返回结果将是一个对象(如果找到),并且由于对象是通过引用而不是通过值传递的,因此更改该对象的属性将更改原始属性对象也是。

编辑:找到最深的对象:

如果你想找到最深的对象,你必须通过对象obj中的每个对象和子对象,每次你必须存储对象及其深度(如果深度为结果大于之前的结果当然)。这是带有一些注释的代码(我使用了一个实际上在所有对象上调用的内部函数_find):

function findItem (item, obj) {
    var found = null;              // the result (initialized to the default return value null)
    var depth = -1;                // the depth of the current found element (initialized to -1 so any found element could beat this one) (matched elements will not be assigned to found unless they are deeper than this depth)

    function _find(obj, d) {       // a function that take an object (obj) and on which depth it is (d)
        for (var key in obj) {     // for each ...
            // first call _find on sub-objects (pass a depth of d + 1 as we are going to a one deep bellow)
            if (typeof obj[key] === 'object' && !Array.isArray(obj[key])) { 
                _find(obj[key], d + 1);
            }
            // then check if this object actually contain the item (we still at the depth d)
            else if (obj[key] === item || (Array.isArray(obj[key]) && obj[key].includes(item))) {
                // if we found something and the depth of this object is deeper than the previously found element
                if(d > depth) {
                    depth = d;     // then assign the new depth
                    found = obj;   // and assign the new result
                }
            }
        }
    }
    _find(obj, 0);                 // start the party by calling _find on the object obj passed to findItem with a depth of 0

    // at this point found is either the initial value (null) means nothing is found or it is an object (the deepest one)
    return found;
}

答案 1 :(得分:0)

那是因为递归调用没有将返回值赋给变量。 你应该检查递归调用的返回值,如果你有其他逻辑,则返回true或者从for循环中断。

 function findItem(item, obj) {
 for (var key in obj) {
     if (obj[key] === item) {
         return obj;
     } else if (Array.isArray(obj[key]) === true && obj[key].includes(item) === true) {
         return obj;
     } else if (typeof obj[key] === 'object' && Array.isArray(obj[key]) === false) {
         var foundItem = findItem(item, obj[key]);
         if(foundItem) 
          return foundItem;
     }
 }

答案 2 :(得分:0)

  

“我试图以递归方式搜索包含字符串,数组和其他对象的对象,以便在最深层找到一个项目(匹配一个值)但是我总是将其定义为返回结果。 “

var foundIt = findItem('glasses', theCobWeb);
console.log('The item is here: ' + foundIt); // The item is here: undefined 
     

“项目在这里......” - 在哪里?

那么你想要什么作为回报值呢?它应该只是说"glasses"什么时候完成?在我看来,这有点毫无意义 - 从根本上来说,仅仅回归truefalse并不是更好。

我刚才写了这个函数,因为我需要搜索一堆数据,但也知道它匹配的位置。我现在可能会对此进行一些修改(或者至少包括类型注释),但它按原样运行,所以在这里你去。

// helpers
const keys = Object.keys
const isObject = x=> Object(x) === x
const isArray = Array.isArray
const rest = ([x,...xs])=> xs

// findDeep
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 {parents: [], path: [], node: undefined}
    else
      return processNode(parents, path, node)
  }
  return loop([make(x)], [])
}

// your sample data
var theCobWeb = {biggestWeb: {item: "comb",biggerWeb: {items: ["glasses", "paperclip", "bubblegum"],smallerWeb: {item: "toothbrush",tinyWeb: {items: ["toenails", "lint", "wrapper", "homework"]}}},otherBigWeb: {item: "headphones"}}};

// find path returns {parents, path, node}
let {path, node} = findDeep((value,key)=> value === "glasses", theCobWeb)

// path to get to the item, note it is in reverse order
console.log(path) // => [0, 'items', 'biggerWeb', 'biggestWeb']

// entire matched node
console.log(node) // => ['glasses', 'paperclip', 'bubblegum']

这里的基本直觉是node[path[0]] === searchTerm

匹配查询的完整路径

我们获得匹配数据的完整密钥路径。这很有用,因为我们根据搜索的根知道确切的位置。要验证路径是否正确,请参阅此示例

const lookup = ([p,...path], x) =>
  (p === undefined) ? x : lookup(path,x)[p]
lookup([0, 'items', 'biggerWeb', 'biggestWeb'], theCobWeb) // => 'glasses'

不匹配的查询

请注意,如果我们搜索未找到的内容,node将为undefined

let {path, node} = findDeep((value,key)=> value === "sonic the hog", theCobWeb)
console.log(path) // => []
console.log(node) // => undefined

搜索特定的键/值对

搜索功能收到valuekey参数。根据需要使用它们

let {path, node} = findDeep((value,key)=> key === 'item' && value === 'toothbrush', theCobWeb)
console.log(path) // => [ 'item', 'smallerWeb', 'biggerWeb', 'biggestWeb' ]
console.log(node) // => { item: 'toothbrush', tinyWeb: { items: [ 'toenails', 'lint', 'wrapper', 'homework' ] } }

短路 - 150cc

哦,因为我破坏了你,findDeep会在找到第一场比赛后立即回复。它不会浪费计算周期,并在知道答案后继续遍历您的数据堆。这是一件好事。

去探索

有勇气,有冒险精神。上面的findDeep函数还为返回的对象提供了parents属性。它在某些方面可能对你有用,但解释起来有点复杂,而且回答这个问题并不是很重要。为了简化这个答案,我只想提到它。