簡體   English   中英

Javascript:當用戶從樹中選擇/取消選擇節點時,如何保留樹結構和祖父子關系

[英]Javascript: How to preserve tree structure and grandparent-child relationship when user selects/deselects nodes from the tree

我有一個樹結構{name, [children]} (JS 對象)。 用戶可以任意 select 任何節點以部分復制結構(復制到 JS 對象數組中,因為可以選擇松散的葉節點)。 例如,用戶可以 select 是葉節點,但不是其父/祖父,那么葉節點將位於最高父級的children級的平面數組中。

例子:

original (JS Object)      selected
   A✓                       A
   |  \ \                  / \
   B✓ C D✓                B  D
  / \          =>         / \
 E✓  F                   E  G
     |
     G✓ 

original                  selected(Array of JS objects)
   A                       B, D      
   |  \ \                 / \
   B✓ C D✓               E  G 
  / \          =>         
 E✓  F                   
     |
     G✓ 

我的第一個想法是:對於單擊的每個節點,假設我們維護一個selected的數組:

  1. 如果此節點處於selected狀態 => 取消選中(相當直接):

    1。 如果節點是“頂級”元素 => 將其從數組中刪除,並將其子節點推送到數組中。

    a2。 如果節點是頂級元素的(大)子節點 => 從其父節點的子節點中刪除該節點,並將該節點的子節點重新附加到其父節點。

  2. 如果此節點未selected => select(發生奇怪的事情):

    一個。 用一個空的children初始化一個tempNode ,因為我們還不知道它的孩子

    灣。 如果這個節點有(大)孩子=>對於每個(大)孩子,如果(大)孩子在selected =>刪除selected的(大)孩子=>將此(大)孩子添加到tempNodechildren (對於多個級別應該是遞歸的)

    c1。 如果這個節點的(大)父節點已經被selected => 將tempNode附加到(大)父節點(應該是多個級別的遞歸)

    c2。 如果該節點的(大)父節點不在selected中,則將其推送到數組中。

我主要停留在步驟 2b 和 2c 上。

//for adding node into selected
const addChildInSelected = (child) => {
    var tempNode = {
        name: child.name,
        children: []
    }
    var clonedState = clone(selected)
    var childInTree = findChildInTree(child.id, props.data)
    //if the child has children, check if they are in selected yet.
    if (childInTree.children.length > 0) {
        //Problem 1: I should run this recursively for all the grandchildren, 
        //but I struggle to find the base case. But recursion might not be the best solution
        for (var i = 0; i < childInTree.children.length; i++) {
            //Problem 2: findChildInSelected/removeChildInSelect are all recursive calls as well,
            //due to the tree structure. very worried about stack overflow during this step...
            var grandChildInSelected = findChildInSelected(childInTree.children[i].id)
            if (grandChildInSelected !== null) {
                clonedState = removeChildInSelected(clonedState)
                tempNode.children.push(findChildInTree(childInTree.children[i]))
            }
        }
    }
    //Problem 3. I realized I needed to check for each node in the `selected` again. 
    //another potential performance hurdle
}

現在我重新考慮這個問題,每個節點只關心它自己、它的直接父/祖父以及它的所有子/孫子作為一個平面數組。 這可能是研究這個問題的一個很好的視角。 也許找到父母/祖父母或所有孩子的助手 function 將是一個好的開始。 (另外我意識到在幾個功能中我忘記檢查祖父母......)

但是由於我使用多個樹結構進行操作,所以下面的 function 似乎並不理想......

//just a quick example
const findDescendantsInSelected = (node, descendants=[]) => {
    //recursive call within recursive function which could overflow if the tree is big
    if (findNodeInSelected(node) !== null) {
        descendents.push(node)
    }
    if (node.children.length > 0) {
        for (var i = 0; i < entry.children.length; i++) {
            result = findDescendantsInSelected(node.children[i], descendants);
        }
        return result
    }
    return descendents
}

總而言之,維護幾乎 3 個獨立的樹(選擇 state、原始數據和克隆,因為我使用了 react)並跟蹤每個步驟中的父母/孩子,跨不同的功能讓我頭疼。 任何有關如何簡化問題的見解將不勝感激!

使用深度優先遍歷的簡單純遞歸 function 就足夠了。 它返回您想要的選定樹的列表(稱為森林)。 如果選擇了一個節點,它將收集所有選定的后代樹並返回一個以這些為子節點的新節點。 如果未選擇該節點,它只會加入其子節點的列表並返回該列表。

function selectedForest(node) {
    const trees = node.children.flatMap(selectedForest);
    return isSelected(node) ? [ {...node, children: trees} ] : trees;
}

演示:

 const demo = { name: "A", children: [ { name: "B", children: [ { name: "E", children: [] }, { name: "F", children: [ { name: "G", children: [] }, ] }, ] }, { name: "C", children: [] }, { name: "D", children: [] }, ] }; let selection; function isSelected({name}) { return selection.has(name); } function selectedForest(node) { const trees = node.children.flatMap(selectedForest); return isSelected(node)? [ {...node, children: trees} ]: trees; } selection = new Set("ABDEG"); console.log(JSON.stringify(selectedForest(demo), null, 2)); selection = new Set("BDEG"); console.log(JSON.stringify(selectedForest(demo), null, 2));

暫無
暫無

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

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