如何从嵌套的对象数组中获取每个父级的值

Posted

技术标签:

【中文标题】如何从嵌套的对象数组中获取每个父级的值【英文标题】:How to get the value of each parent from a nested array of object 【发布时间】:2021-10-27 11:43:55 【问题描述】:

所以我有多个对象数组,每个对象都包含一个子对象。

例如

const data = [
    
        id: 1,
        name: 'parent 1',
        children: [
            
                id: 'c1',
                name: 'child 1',
                children: [
                    
                        id: 'g1',
                        name: 'grand 1',
                        children: [],
                    ,
                ],
            ,
        ],
    ,
    
        id: 2,
        name: 'parent 2',
        children: [
            
                id: 2,
                name: 'c1',
                children: [],
            ,
        ],
    ,
     id: 3, name: 'parent 3', children: [] ,
];

我想要发生的是,如果我正在搜索的 Id 是 'g1',我会得到结果

const result = ['parent 1', 'c1', 'grand 1']

循环只会停止并获取它通过的所有名称,直到满足条件(在本例中为 id)

当前方法已完成

/**
 * Details
 * @param id the value you are searching for
 * @param items nested array of object that has child
 * @param key name of the value you are looking for
 * @returns string of array that matches the id
 * @example ['parent 1', 'c1', 'grand 1']
 */
export function findAll(id: string, items: any, key: string): string[] 
  let i = 0;
  let found;
  let result = [];

  for (; i < items.length; i++) 
    if (items[i].id === id) 
      result.push(items[i][key]);
     else if (_.isArray(items[i].children)) 
      found = findAll(id, items[i].children, key);
      if (found.length) 
        result = result.concat(found);
      
    
  
  return result;


【问题讨论】:

你试过什么?另外,const result = 'parent 1' &gt; 'c1' &gt; 'grand 1'这是无效的数据对象,你可以验证你想要什么数据结构。 @ikhvjs,我更新了我想看的结果,谢谢评论 到目前为止你尝试过什么?我认为递归函数方法适合您的情况。 @ikhvjs,我尝试了这个,第三个答案,***.com/questions/30714938/… 并进行了一些修改,但我似乎找不到将父母姓名存储在结果数组中的方法 你能告诉我们你的方法吗? 【参考方案1】:

我编写了这段迭代代码,可能会对您有所帮助。它基本上遍历存储从顶层到所需id的路径的结构:

function getPath(obj, id) 
    // We need to store path
    // Start stack with root nodes
    let stack = obj.map(item => (path: [item.name], currObj: item));
    while (stack.length) 
        const path, currObj = stack.pop()
        if (currObj.id === id) 
            return path;
         else if (currObj.children?.length) 
            stack = stack.concat(currObj.children.map(item => (path: path.concat(item.name), currObj: item)));
        
    
    return null; // if id does not exists

此代码假定您的结构是正确的并且没有遗漏任何部分(除了可以为 null 的子代)。 顺便说一句,你的答案正确吗?我想路径应该是: ["parent 1", "child 1", "grand 1"]

【讨论】:

【参考方案2】:

下面的解决方案是执行搜索的递归函数。

const data = [
    
        id: 1,
        name: 'parent 1',
        children: [
            
                id: 'c1',
                name: 'child 1',
                children: [
                    
                        id: 'g1',
                        name: 'grand 1',
                        children: [],
                    ,
                ],
            ,
        ],
    ,
    
        id: 2,
        name: 'parent 2',
        children: [
            
                id: 2,
                name: 'c1',
            ,
        ],
    ,
     id: 3, name: 'parent 3', children: [] ,
];

function getPath(object, search) 
    if (object.id === search) return [object.name];
    else if ((object.children) || Array.isArray(object)) 
        let children = Array.isArray(object) ? object : object.children;
        for (let child of children) 
            let result = getPath(child, search);
            if (result) 
                if (object.id )result.unshift(object.name);
                return result;
            
        
    


//const result = ['parent 1', 'c1', 'grand 1']
const result = getPath(data, 'g1');
console.log(result);

【讨论】:

嗨 Lajos,当数据结构中只有一个“g1”时,它工作正常。如果在另一组 JSON 中的最后一个子节点中存在相同的“g1”怎么办。我的数据结构中没有名为 id 的单独键。我可能有重复的数据。现在如何提取我想要的确切数据集。? @JohnsonAnthony 我对您的问题的理解是您有一个没有id 的 JSON,并且您可能有重复的结果。如果我正确理解您的问题,那么您想找到所有匹配项。这是一个准确的理解吗?另外,您可以创建一个 JSFiddle 并在评论部分分享链接,或者提出一个单独的问题并在评论部分分享链接?如果您今天创建它,那么我可能会在今天或明天研究它。【参考方案3】:

您可以编写一个递归函数,在将遍历的对象累积到堆栈中时遍历数组。一旦你得到一个带有你想要(id == g1) 的 id 的对象,你就可以打印解决方案。可能是这样的:

'use strict';

function print(stack) 
    //console.log("Printing result...\n");
    let result = "";
    stack.forEach(element => 
        result += element["name"] + " > ";
    );
    console.log(result + "\n");


function walkThrough(data, id, stack) 
    if (data !== undefined)
    for (let i = 0; i < data.length; i++) 
        const element = data[i];
        //console.log("Going through " + element["name"] + " with id == " + element["id"]);
        stack.push(element);
        if (element["id"] == id) print(stack);
        else walkThrough(element["children"], id, stack);            
    


const data = [
    
        "id": 1,
        "name": 'parent 1',
        "children": [
            
                "id": 'c1',
                "name": 'child 1',
                "children": [
                    
                        "id": 'g1',
                        "name": 'grand 1',
                        "children": [],
                    ,
                ],
            ,
        ],
    ,
    
        "id": 2,
        "name": 'parent 2',
        "children": [
            
                "id": 2,
                "name": 'c1',
            ,
        ],
    ,
     "id": 3, "name": 'parent 3', "children": [] ,
];
//Calling the function to walk through the array...
walkThrough(data, 'g1', []);

【讨论】:

【参考方案4】:

另一种方法(不像上述解决方案那么优雅,但也可以)

非常直接: 使用for 迭代循环到第 3 级,当在第 3 级找到孙子时,break 以逃避所有 3 级。

我很好奇不同的解决方案如何比较大型数据集(比如一百万条记录)的性能。

let i=0, k=0, l=0;

let childrenLength = 0, grandChildrenLength = 0;

let result = [];
let foundGrandChild = false;

function searchGrandChild(searchString) 

  for (i; i< data.length; ++i) 
    if(data.length > 0)
      childrenLength = data[i].children.length;

      if(childrenLength > 0) 
        for (k; k < childrenLength; ++k) 

          if(data[i].children[k] != undefined) 
            grandChildrenLength = data[i].children[k].children.length;

            if(grandChildrenLength > 0) 
              for (l; l < grandChildrenLength; ++l) 
                if(data[i].children[k].children[l] != undefined) 

                  if(data[i].children[k].children[l].id === searchString) 
                    result.push(data[i].name);
                    result.push(data[i].children[k].id);
                    result.push(data[i].children[k].children[l].name);
                    foundGrandChild = true;
                    console.log('Yap, we found your grandchild ?')
                    console.log(result);
                    break;
                  
                
                if(foundGrandChild) break;
              

            
          
          if(foundGrandChild) break;
        

      
    
    if(foundGrandChild) break;
  

  if(!foundGrandChild) console.log('sorry, we could not find your grandchild ?')
;

const data = [
    
        id: 1,
        name: 'parent 1',
        children: [
            
                id: 'c1',
                name: 'child 1',
                children: [
                    
                        id: 'g1',
                        name: 'grand 1',
                        children: [],
                    ,
                ],
            ,
        ],
    ,
    
        id: 2,
        name: 'parent 2',
        children: [
            
                id: 2,
                name: 'c1',
            ,
        ],
    ,
     id: 3, name: 'parent 3', children: [] ,
];


console.log('Let us search for "g1" ...');
searchGrandChild('g1');

console.log('Let us now search for "g2" ...');
foundGrandChild = false;
searchGrandChild('g2');

【讨论】:

以上是关于如何从嵌套的对象数组中获取每个父级的值的主要内容,如果未能解决你的问题,请参考以下文章

如何从包含数组的对象数组中获取不同的值

从Mongo DB嵌套数组中获取不同的值并输出到单个数组

如何获取从 Knex 返回的值(数组中的对象,即数组中的对象)

如何从 React 中的嵌套对象数组中提取数据?

从嵌套属性数组中获取对象嵌套值

如何从自定义模型对象数组中获取特定键的值