过滤JSON中的一个特定节点,显示直接父级和所有子级

时间:2019-06-28 12:28:31

标签: javascript json

给出以下JSON对象;

const data = [
    { "id": 1, "name": "Node 1", "children": [
            { "id": 3, "name": "Node 1.1", "children": [
                    { "id": 6, "name": "Node 1.1.1", "children": [
                            { "id": 12, "name": "Node 1.1.1.1", "children": [] }
                        ]
                    },
                    { "id": 7, "name": "Node 1.1.2", "children": [
                            { "id": 13, "name": "Node 1.1.2.1", "children": [] }
                        ]
                    }
                ]
            },
            { "id": 4, "name": "Node 1.2", "children": [
                { "id": 8, "name": "Node 1.2.1", "children": [] },
                { "id": 9, "name": "Node 1.2.2", "children": [
                        { "id": 14, "name": "Node 1.2.2.1", "children": [] },
                        { "id": 15, "name": "Node 1.2.2.2", "children": [] }
                    ]
                }
            ]
        }
        ]
    },
    { "id": 2, "name": "Node 2", "children": [
            { "id": 5, "name": "Node 2.1", "children": [
                    { "id": 10, "name": "Node 2.1.1", "children": [] },
                    { "id": 11, "name": "Node 2.1.2", "children": [
                            { "id": 16, "name": "Node 2.1.2.1", "children": [] }
                        ]
                    }
                ]
            }
        ]
    }
];

我希望能够通过ID查找特定的节点,并且一旦找到该节点,便获得其直接父级和所有子级。例如,如果我想找到ID为9的节点(节点1.2.2),则希望它返回Node 1Node 1.2Node 1.2.2及其子节点,并忽略其他所有内容。我已经部分使用此findById函数;

findById(data, id)
{
    let node = '';
    data.some((currentNode) => {
        return node = id === currentNode.id ? currentNode : this.findById(currentNode.children, id);
    });
    return node;
}

这样称呼;

this.data = [this.findById(this.data, id)];

但是它并不能完全满足我的要求。它找到正确的节点(因此在本例中为1.2.2)及其子节点(1.2.2.1和1.2.2.2),但找不到其直接父节点(1.2和1)。我该如何更改findById函数以使其也包括直接父母?

所需的输出为;

const found = [
    { "id": 1, "name": "Node 1", "children": [
            { "id": 4, "name": "Node 1.2", "children": [
                    { "id": 9, "name": "Node 1.2.2", "children": [
                            { "id": 14, "name": "Node 1.2.2.1", "children": [] },
                            { "id": 15, "name": "Node 1.2.2.2", "children": [] }
                        ]
                    }
                ]
            }
        ]
    }
];

4 个答案:

答案 0 :(得分:2)

您只需要存储递归函数的结果。为此,将您的三元分解成一个if,如下所示:

function findById(data, id) {
    let node = null;

    data.some((currentNode) => {
        if (id === currentNode.id) {
            return node = [currentNode];
        }

        const inItsTree = findById(currentNode.children, id);
        if (inItsTree) {
            return node = [{ ...currentNode, children: inItsTree }];
        }
    });

    return node;
}

const data = [{"id":1,"name":"Node 1","children":[{"id":3,"name":"Node 1.1","children":[{"id":6,"name":"Node 1.1.1","children":[{"id":12,"name":"Node 1.1.1.1","children":[]}]},{"id":7,"name":"Node 1.1.2","children":[{"id":13,"name":"Node 1.1.2.1","children":[]}]}]},{"id":4,"name":"Node 1.2","children":[{"id":8,"name":"Node 1.2.1","children":[]},{"id":9,"name":"Node 1.2.2","children":[{"id":14,"name":"Node 1.2.2.1","children":[]},{"id":15,"name":"Node 1.2.2.2","children":[]}]}]}]},{"id":2,"name":"Node 2","children":[{"id":5,"name":"Node 2.1","children":[{"id":10,"name":"Node 2.1.1","children":[]},{"id":11,"name":"Node 2.1.2","children":[{"id":16,"name":"Node 2.1.2.1","children":[]}]}]}]}];

console.log(findById(data, 9));

答案 1 :(得分:0)

第一个问题是,这是正确的数据结构吗?您可以在链接列表中实现此功能吗?如果是这样,您可以对祖父母节点(父节点)有特定的引用。如果数据相对较小,则搜索时间是线性的,插入和删除是线性的。

class Node {
  constructor(value){
    this.value = value;
    this.next = null;
    this.children = [];
  }
}

class LinkedList {
  constructor() {
    this.head = null;
    this.tail = null;
    this.size = 0
    }

  insert(value) {
    let node = new Node(value);
    if(!this.head) {
      this.head = node;
    } else {
      this.tail.next = node;
    }
    this.tail = node;
    this.size++;
  }

  findNode(value) {
    let node = this.head;
    while(!!node) {
      if(node.value === value){
        return node;
      }
      node = node.next;
    }
    return `no node with ${value} found`
  }

  forEach(callback) {
    let node = this.head;
    while(!!node) {
      callback(node.value);
      node = node.next;
    }
  }

  print() {
    if(!this.head) {
      return null
    } else {
      let node = this.head;
      while(!!node){
        console.log(node.value);
        node = node.next
      }
    }

答案 2 :(得分:0)

将您的findById方法修改为:

function findById(data, id, parent)
{
    let node = '';
    data.some((currentNode) => {
      if(id === currentNode.id) {
        node = currentNode;
      } else {
        parent.push(currentNode); // track parent node
        node = this.findById(currentNode.children, id, parent);
      }
      return node;
    });
    return {node: node, parent: parent};
}

this.data = [this.findById(this.data, 6, [])];

console.log(this.data[0].node); // will give you current node with children
console.log(this.data[0].parent); // will give you array of parents node

答案 3 :(得分:0)

递归是一种功能性遗产,因此这是使用功能性样式的解决方案-

// identity : 'a -> 'a
const identity = x =>
  x

// findById : (number, node array) -> node?
const findById = (q = 0, [ n, ...more ], exit = identity) =>
  n === undefined
    ? false
    : findById1 (q, n, exit)
      || findById (q, more, exit)

// findById1 : (number, node) -> node?
const findById1 = (q = 0, n = {}, exit = identity) =>
  n.id === q
    ? exit (n)
    : findById (q, n.children, r =>
        exit ({...n, children: [ r ] })
      )

它是这样的-

findById (9, data)
// { id: 1, name: "Node 1", children: [
//   { id: 4, name: "Node 1.2", children: [
//     { id: 9, name: "Node 1.2.2", children: [
//         { id: 14, name: "Node 1.2.2.1", children: [] },
//         { id: 15, name: "Node 1.2.2.2", children: [] }
//     ]}
//   ]}
// ]}

找不到ID时,返回false-

findById (99, data)
// false

展开以下代码段,以在您自己的浏览器中验证结果-

const identity = x =>
  x

const findById = (q = 0, [ n, ...more ], exit = identity) =>
  n === undefined
    ? false
    : findById1 (q, n, exit)
      || findById (q, more, exit)

const findById1 = (q = 0, n = {}, exit = identity) =>
  n.id === q
    ? exit (n)
    : findById (q, n.children, r =>
        exit ({...n, children: [ r ] })
      )

const data =
  [{id:1, name:"Node 1", children:[{id:3, name:"Node 1.1", children:[{id:6, name:"Node 1.1.1", children:[{id:12, name:"Node 1.1.1.1", children:[]}]},{id:7, name:"Node 1.1.2", children:[{id:13, name:"Node 1.1.2.1", children:[]}]}]},{id:4, name:"Node 1.2", children:[{id:8, name:"Node 1.2.1", children:[]},{id:9, name:"Node 1.2.2", children:[{id:14, name:"Node 1.2.2.1", children:[]},{id:15, name:"Node 1.2.2.2", children:[]}]}]}]},{id:2, name:"Node 2", children:[{id:5, name:"Node 2.1", children:[{id:10, name:"Node 2.1.1", children:[]},{id:11, name:"Node 2.1.2", children:[{id:16, name:"Node 2.1.2.1", children:[]}]}]}]}]

console.log (findById (9, data))
// { id: 1, name: "Node 1", children: [
//   { id: 4, name: "Node 1.2", children: [
//     { id: 9, name: "Node 1.2.2", children: [
//         { id: 14, name: "Node 1.2.2.1", children: [] },
//         { id: 15, name: "Node 1.2.2.2", children: [] }
//     ]}
//   ]}
// ]}

console.log (findById (99, data))
// false


连续传递样式泄漏了函数界面中的exit参数。使用帮助程序功能,我们可以创建与您的原始API匹配的API-

// export findById : (number, node array) -> node? 
const findById = (q = 0, nodes = []) =>
  findAny (q, nodes, identity)

// private
const findAny = (q, [ node, ...more ], exit) =>
  node === undefined
    ? false
    : find1 (q, node, exit)
      || findAny (q, more, exit)

// private
const find1 = (q, node, exit) =>
  node.id === q
    ? exit (node)
    : findAny (q, node.children, r =>
        exit ({...node, children: [ r ] })
      )

最后,我们可以修改原始实现以创建更高阶的findBy,而不是接受id输入,它接受类似于Array.prototype.find的功能-

// export findBy : ('node -> bool, node array) -> node?
const findBy = (f = identity, nodes = []) =>
  findAny (f, nodes, identity)

// private
const findAny = (f, [ node, ...more ], exit) =>
  node === undefined
    ? false
    : find1 (f, node, exit)
      || findAny (f, more, exit)

// private
const find1 = (f, node, exit) =>
  f (node)
    ? exit (node)
    : findAny (f, node.children, r =>
        exit ({...node, children: [ r ] })
      )

通过专用findById-

可以轻松实现更具体的功能,例如findByNamefindBy
// export findById : (number, node array) -> node? 
const findById = (q = 0, nodes = []) =>
  findBy (n => n.id === q, nodes, identity)

// export findByName : (string, node array) -> node? 
const findByName = (q = "", nodes = []) =>
  findBy (n => n.name === q, nodes, identity)

或者您可以通过任意东西找到-

findBy (node => node.id > 100 && node.name.length < 20, data)
// ...

希望这开始说明了功能样式的一些优点。