簡體   English   中英

從深度嵌套的對象數組中刪除匹配的對象

[英]Remove matched object from deeply nested array of objects

我有一個帶孩子的數據樹結構:

{  id: 1,
   name: "Dog",
   parent_id: null,
   children: [
         {
             id: 2,
             name: "Food",
             parent_id: 1,
             children: []
         },
         {
             id: 3,
             name: "Water",
             parent_id: 1,
             children: [
                 {
                    id: 4,
                    name: "Bowl",
                    parent_id: 3,
                    children: []
                 },
                 {
                    id: 5,
                    name: "Oxygen",
                    parent_id: 3,
                    children: []
                 },
                 {
                    id: 6,
                    name: "Hydrogen",
                    parent_id: 3,
                    children: []
                 }
             ]
         }
   ]
}

這表示一個 DOM 結構,用戶可以通過單擊 DOM 中的相應按鈕從中選擇要刪除的項目。

我有一個選定項目的已知文本標題,用於從 DOM 中刪除,設置為變量 clickedTitle。 我無法找到一種算法來允許我從深度嵌套的樹中刪除正確的對象數據。

這是我的代碼:

function askUserForDeleteConfirmation(e) {
    const okToDelete = confirm( 'Are you sure you want to delete the item and all of its sub items?' );
    if(!okToDelete) {
        return;
    }
    const tree = getTree(); // returns the above data structure
    const clickedTitle = getClickedTitle(e); // returns string title of clicked on item from DOM - for example "Dog" or "Bowl"
    const updatedTree = removeFromTree(tree, tree, clickedTitle);

    return updatedTree;
}

function removeFromTree(curNode, newTree, clickedTitle) {
    if(curNode.name === clickedTitle) {
        // this correctly finds the matched data item to delete but the next lines don't properly delete it... what to do?
        const index = curNode.children.findIndex(child => child.name === clickedTitle);
        newTree = curNode.children.slice(index, index + 1);
        // TODO - what to do here?
    }

    for(const node of curNode.children) {
        removeFromTree(node, newTree, clickedTitle);
    }

    return newTree;
}

我嘗試使用 javascript 從對象數組中刪除匹配對象中的信息,但沒有成功。

如果您不介意就地修改參數樹,這應該可以完成工作。 請注意,如果您嘗試刪除根,它將返回null

 const tree = { id: 1, name: "Dog", parent_id: null, children: [ { id: 2, name: "Food", parent_id: 1, children: [] }, { id: 3, name: "Water", parent_id: 1, children: [ { id: 4, name: "Bowl", parent_id: 3, children: [] }, { id: 5, name: "Oxygen", parent_id: 3, children: [] }, { id: 6, name: "Hydrogen", parent_id: 3, children: [] } ] } ] }; const removeFromTree = (root, nameToDelete, parent, idx) => { if (root.name === nameToDelete) { if (parent) { parent.children.splice(idx, 1); } else return null; } for (const [i, e] of root.children.entries()) { removeFromTree(e, nameToDelete, root, i); } return tree; }; console.log(removeFromTree(tree, "Oxygen"));

您當前的代碼非常正確。 然而:

newTree = curNode.children.slice(index, index + 1);

突出幾個問題:我們需要操縱父級的children數組來刪除curNode而不是curNode自己的children數組。 我通過調用遞歸地傳遞父對象和子索引,省去了線性操作findIndex的麻煩。

此外,從indexindex + 1切片只會提取一個元素並且不會修改curNode.children 如何使用newArray或通過調用堆棧返回它並不明顯。 splice似乎是更適合手頭任務的工具:就地提取一個元素。

請注意,此函數將刪除多個匹配nameToDelete條目。

我已經構建了如下算法:

function omitNodeWithName(tree, name) {
  if (tree.name === name) return undefined;

  const children = tree.children.map(child => omitNodeWithName(child, name))
    .filter(node => !!node);

  return {
    ...tree,
    children
  }  
}

您可以使用它來返回沒有該項目的新樹:

noHydrogen = omitNodeWithName(tree, "Hydrogen")

我喜歡@VictorNascimento 的回答,但是通過應用map然后filter ,每個children列表將被迭代兩次。 這是使用reduce避免這種情況的替代方法:

function removeFromTree(node, name) {
  return node.name == name
    ? undefined
    : {
        ...node,
        children: node.children.reduce(
          (children, child) => children.concat(removeFromTree (child, name) || []), [])
      }
}

如果您想要一種就地刪除項目的方法,如@ggorlen 所建議的,我建議使用以下解決方案,我認為這更簡單:

function removeFromTree(node, name) {
  if (node.name == name) {
    node = undefined
  } else {
    node.children.forEach((child, id) => {
      if (!removeFromTree(child, name)) node.children.splice(id, 1)
    })
  }
  return node
}

如果可以使用 Lodash+ Deepdash ,那么:

let cleaned = _.filterDeep([tree],(item)=>item.name!='Hydrogen',{tree:true});

這是一個代碼筆

我們將對象掃描用於許多數據處理任務。 一旦你將頭環繞在它周圍,它就會變得強大。 這是你如何回答你的問題

 // const objectScan = require('object-scan'); const prune = (name, input) => objectScan(['**[*]'], { rtn: 'bool', abort: true, filterFn: ({ value, parent, property }) => { if (value.name === name) { parent.splice(property, 1); return true; } return false; } })(input); const obj = { id: 1, name: 'Dog', parent_id: null, children: [{ id: 2, name: 'Food', parent_id: 1, children: [] }, { id: 3, name: 'Water', parent_id: 1, children: [{ id: 4, name: 'Bowl', parent_id: 3, children: [] }, { id: 5, name: 'Oxygen', parent_id: 3, children: [] }, { id: 6, name: 'Hydrogen', parent_id: 3, children: [] }] }] }; console.log(prune('Oxygen', obj)); // return true iff pruned // => true console.log(obj); // => { id: 1, name: 'Dog', parent_id: null, children: [ { id: 2, name: 'Food', parent_id: 1, children: [] }, { id: 3, name: 'Water', parent_id: 1, children: [ { id: 4, name: 'Bowl', parent_id: 3, children: [] }, { id: 6, name: 'Hydrogen', parent_id: 3, children: [] } ] } ] }
 .as-console-wrapper {max-height: 100% !important; top: 0}
 <script src="https://bundle.run/object-scan@13.8.0"></script>

免責聲明:我是對象掃描的作者

暫無
暫無

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

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