javascript查找深层嵌套的对象

时间:2018-11-19 20:11:11

标签: javascript lodash

我需要使用javascript(可能是在lodash的帮助下)以深度嵌套的对象数组递归过滤对象。 什么是最干净的方法,如果我不知道数组中将有多少个嵌套对象?

假设我具有以下结构

totalVal = topItems["Price"].sum()
topFiveItems = pd.DataFrame({
    "Item Name": itemNames,
    "Purchase Count": purchaseCount,
    "Item Price": itemPrices.map("${:.2f}".format),
    "Total Purchase Value": totalVal.map("${:.2f}".format)
})
topFiveItems.sort_values(ascending = False, by = "Total Purchase Value").head()

我想找到带有[ { label: "first", id: 1, children: [] }, { label: "second", id: 2, children: [ { label: "third", id: 3, children: [ { label: "fifth", id: 5, children: [] }, { label: "sixth", id: 6, children: [ { label: "seventh", id: 7, children: [] } ] } ] }, { label: "fourth", id: 4, children: [] } ] } ]; 的那个,如果有子对象,则返回true,否则返回false。

当然,如果我具有类似的数据结构,但项目数不同,那么它也应该可以工作。

10 个答案:

答案 0 :(得分:5)

由于您只需要true中的false个答案,因此可以在递归上使用some(),有效地进行深度优先搜索,并且使其简洁:

let arr = [{label: "first",id: 1,children: []},{label: "second",id: 2,children: [{label: "third",id: 3,children: [{label: "fifth",id: 5,children: []},{label: "sixth",id: 6,children: [{label: "seventh",id: 7,children: []}]}]},{label: "fourth",id: 4,children: []}]}];

function findNested(arr, id) {
    let found = arr.find(node => node.id === id)
    return found 
      ? found.children.length > 0 
      : arr.some((c) => findNested(c.children, id))

} 

console.log(findNested(arr, 6))  // True: found with children
console.log(findNested(arr, 7))  // False: found no children
console.log(findNested(arr, 97)) // False: not found

答案 1 :(得分:1)

也许递归式的解决方案可能对您有用?在此,通过提供的输入数据的“子级”递归搜索具有提供的id的节点。如果找到具有匹配ID的子节点,则根据该节点children数组中数据的存在情况返回布尔结果:

function nodeWithIdHasChildren(children, id) {
  
  for(const child of children) {

    // If this child node matches supplied id, then check to see if
    // it has data in it's children array and return true/false accordinly
    if(child.id === id) {
    
      if(Array.isArray(child.children) && child.children.length > 0) {
        return true
      }
      else {
        return false
      }
    }
    else {
    
      const result = nodeWithIdHasChildren(child.children, id);

      // If result returned from this recursion branch is not undefined
      // then assume it's true or false from a node matching the supplied
      // id. Pass the return result up the call stack
      if(result !== undefined) {
        return result
      }
    }
  }  
}

const data = [
  {
    label: "first",
    id: 1,
    children: []
  },
  {
    label: "second",
    id: 2,
    children: [
      {
        label: "third",
        id: 3,
        children: [
          {
            label: "fifth",
            id: 5,
            children: []
          },
          {
            label: "sixth",
            id: 6,
            children: [
              {
                label: "seventh",
                id: 7,
                children: []
              }
            ]
          }
        ]
      },
      {
        label: "fourth",
        id: 4,
        children: []
      }
    ]
  }
];



console.log('node 6 has children:', nodeWithIdHasChildren( data, 6 ) )

console.log('node 7 has children:', nodeWithIdHasChildren( data, 7 ) )

console.log('node 100 has children:', nodeWithIdHasChildren( data, 7 ), '(because node 100 does not exist)' )

答案 2 :(得分:0)

还:

function explore(myArray, searchedId) {
    for (let i=0; i<myArray.length; i++) {
        let el;
        if (myArray[i].id == searchedId) {
            el = myArray[i];
        } else {
            el = explore(myArray[i].children, searchedId);
        }
        if (el) {
            return el.children.length > 0;
        }
    }
}    

答案 3 :(得分:0)

您可以使用如下所示的“递归”来检查id是否有孩子

let arr = [{label: "first",id: 1,children: []},{label: "second",id: 2,children: [{label: "third",id: 3,children: [{label: "fifth",id: 5,children: []},{label: "sixth",id: 6,children: [{label: "seventh",id: 7,children: []}]}]},{label: "fourth",id: 4,children: []}]}];

function hasChildren(arr, id) {
  let res = false
  for (let d of arr) {
    if(d.id == id) return d.children.length > 0
    res = res || hasChildren(d.children, id)
    if(res) return true
  }
  return res
}

console.log('id 4 has children? ', hasChildren(arr, 4))
console.log('id 6 has children? ', hasChildren(arr, 6))

答案 4 :(得分:0)

您可以使用三个简单的javascript函数来做到这一点:

// Function to Flatten results
var flattenAll = function(data) {
  var result = [];
  var flatten = function(arr) {
    _.forEach(arr, function(a) {
      result.push(a);
      flatten(a.children);
    });
  };
  flatten(data);
  return result;  
};

// Function to search on flattened array
var search = function(flattened, id) {
  var found = _.find(flattened, function(d) { 
              return d.id == id;
            });
  return found;
};

// Function to check if element is found and have children
var hasChildren = function(element) {
  return element && element.children && element.children.length > 0;
}

// Usage, search for id = 6
hasChildren(search(flattenAll(your_data_object), 6))

Plunker

答案 5 :(得分:0)

这是使用recursion并仅通过一个Array.find进行的另一种解决方案:

const data = [ { label: "first", id: 1, children: [] }, { label: "second", id: 2, children: [ { label: "third", id: 3, children: [ { label: "fifth", id: 5, children: [] }, { label: "sixth", id: 6, children: [ { label: "seventh", id: 7, children: [] } ] } ] }, { label: "fourth", id: 4, children: [] } ] } ];

const search = (data, id) => {
  var f, s = (d, id) => d.find(x => x.id == id ? f = x : s(x.children, id))	
  s(data, id)
  return f ? f.children.length > 0 : false
}

console.log(search(data, 6))  // True: found with children
console.log(search(data, 7))  // False: found but has no children
console.log(search(data, 15)) // False: not found at all

这个想法是要有一个递归函数,当找到id时会记住该对象。

一旦找到(或者我们知道没有找到条目),就返回子项array length或返回false

如果您想实际返回found object而不是children.length的布尔值:

const data = [ { label: "first", id: 1, children: [] }, { label: "second", id: 2, children: [ { label: "third", id: 3, children: [ { label: "fifth", id: 5, children: [] }, { label: "sixth", id: 6, children: [ { label: "seventh", id: 7, children: [] } ] } ] }, { label: "fourth", id: 4, children: [] } ] } ];

const search = (data, id) => {
  var f, s = (d, id) => d.find(x => x.id == id ? f = x : s(x.children, id))	
  s(data, id)
  return f
}

console.log(search(data, 6))  // returns only the object with id:6
console.log(search(data, 7))  // returns only the object with id: 7
console.log(search(data, 71)) // returns undefined since nothing was found

答案 6 :(得分:0)

您可以使用generator function来递归地迭代节点,并通过使用Array.prototype.some()简化用于检查是否存在的逻辑:

const data = [{label:'first',id:1,children:[]},{label:'second',id:2,children:[{label:'third',id:3,children:[{label:'fifth',id:5,children:[]},{label:'sixth',id:6,children:[{label:'seventh',id:7,children:[]}]}]},{label:'fourth',id:4,children:[]}]}];

function * nodes (array) {
  for (const node of array) {
    yield node;
    yield * nodes(node.children);
  }
}

const array = Array.from(nodes(data));

console.log(array.some(node => node.id === 6 && node.children.length > 0));
console.log(array.some(node => node.id === 7 && node.children.length > 0));

答案 7 :(得分:0)

JSON.parse reviver parameterJSON.stringify replacer parameter可用于检查所有值,并生成引用节点的平面ID查找对象:

var lookup = {}, json = '[{"label":"first","id":1,"children":[]},{"label":"second","id":2,"children":[{"label":"third","id":3,"children":[{"label":"fifth","id":5,"children":[]},{"label":"sixth","id":6,"children":[{"label":"seventh","id":7,"children":[]}]}]},{"label":"fourth","id":4,"children":[]}]}]'

var result = JSON.parse(json, (key, val) => val.id ? lookup[val.id] = val : val);

console.log( 'id: 2, children count:', lookup[2].children.length )
console.log( 'id: 6, children count:', lookup[6].children.length )
console.log( lookup )

答案 8 :(得分:0)

我建议对deepdash使用lodash扩展名:

var id6HasChildren = _.filterDeep(obj,
  function(value, key, parent) {
    if (key == 'children' && parent.id == 6 && value.length) return true;
  },
  { leavesOnly: false }
).length>0;

这里是docs for filterDeep

根据您的情况,这是full test

答案 9 :(得分:0)

我们现在使用object-scan来满足这样的数据处理需求。一旦将头缠绕在它上,它就会非常强大。这是解决问题的方法

const objectScan = require('object-scan');

const hasChildren = (e) => e instanceof Object && Array.isArray(e.children) && e.children.length !== 0;
const find = (id, input) => {
  const match = objectScan(['**'], {
    abort: true,
    rtn: 'value',
    filterFn: ({ value }) => value.id === id
  })(input);
  return hasChildren(match);
};

const data = [{"label":"first","id":1,"children":[]},{"label":"second","id":2,"children":[{"label":"third","id":3,"children":[{"label":"fifth","id":5,"children":[]},{"label":"sixth","id":6,"children":[{"label":"seventh","id":7,"children":[]}]}]},{"label":"fourth","id":4,"children":[]}]}];

console.log(find(6, data));
// => true
console.log(find(2, data));
// => true
console.log(find(7, data));
// => false
console.log(find(999, data));
// => false