簡體   English   中英

過濾JSON中的一個特定節點,顯示直接父級和所有子級

[英]Filter for one specific node in JSON, show direct parents and all children

給定以下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及其子Node 1.2.2 ,而忽略其他所有Node 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": [] }
                        ]
                    }
                ]
            }
        ]
    }
];

您只需要存儲遞歸函數的結果。 為此,將您的三元分解成一個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)); 

第一個問題是,這是正確的數據結構嗎? 您可以在鏈接列表中實現此功能嗎? 如果是這樣,您可以對祖父母節點(父節點)有特定的引用。 如果數據相對較小,則搜索時間是線性的,插入和刪除是線性的。

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

將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

遞歸是一種功能性遺產,因此這里是使用功能性風格的解決方案-

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

// 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 ,它接受一個類似於Array.prototype.find的函數,而不是接受id輸入。

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

更具體的功能(如findByIdfindByName可以通過專門設置findBy輕松實現-

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

希望這開始說明功能樣式的一些優點。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM