如何通过键将子对象递归追加到父对象

时间:2019-04-30 16:01:55

标签: javascript object recursion

我有一个扁平的对象结构,该结构概述了哪些对象具有父对象,哪些对象没有父对象。现在,我需要将其格式化为正确的结构,以便可以循环浏览并执行功能。

只需添加,然后再进行详细说明。.我知道对象结构不一致,我仍在努力确保所有单个项目都一致。最重要的是孩子要与父母结盟。

我在网上搜索过,但似乎找不到符合我确切需要的任何内容。无论现在好坏,这种结构都是我现在所坚持的。

我对这段代码不满意,它会循环并将子代追加到适当的父代,并删除旧密钥。在这种特定情况下,我忘记了递归,也无法解决这个问题。

var tree = hierarchy.tree, key;
for (key in tree) {
  if (tree.hasOwnProperty(key)) tree[key].children = {};
}
iterate();
return tree;

function iterate() {
  var field, node;
  for (field in tree) {
    if (tree.hasOwnProperty(field)) {
      node = tree[field];
      if (node.parent !== undefined) {
        tree[node.parent].children[field] = node;
        delete tree[field];
      }
    }
  }
}

这是基本结构:

var tree = {
  "submit": {
    "order": 0,
    "field": "submit"
  },
  "hc3qu2nf4": {
    "label": "title",
    "parent": "",
    "order": 0,
    "field": "title",
    "options": {
      "Font Size": "25px",
      "Font Weight": "Bold",
      "Font Style": "Italic",
      "Color": "rgb(64, 128, 128)"
    }
  },
  "dhthivju9": {
    "label": "divider",
    "parent": "",
    "order": 1,
    "field": "divider",
    "options": {
      "height": "14px",
      "color": "#21ce09",
      "width": "50%"
    }
  },
  "z5o9m7sgx": {
    "label": "",
    "parent": "4hhsi94n7",
    "order": 0,
    "field": "col"
  },
  "85ugwci2c": {
    "label": "",
    "parent": "4hhsi94n7",
    "order": 1,
    "field": "col"
  },
  "4hhsi94n7": {
    "label": "column",
    "parent": "",
    "order": 2,
    "field": "column"
  },
  "sbf0bg1o7": {
    "label": "month",
    "parent": "z5o9m7sgx",
    "order": 0,
    "field": "month",
    "options": {
      "start": "2019-04"
    },
    "required": true
  },
  "c3bwnyjmg": {
    "label": "",
    "parent": "4hhsi94n7",
    "order": 2
  },
  "n5m9d84dg": {
    "label": "number",
    "parent": "85ugwci2c",
    "order": 0,
    "field": "number",
    "options": {
      "start": "5",
      "min": "5",
      "max": "10",
      "step": "2"
    }
  },
  "krfxfnzsr": {
    "label": "date",
    "parent": "c3bwnyjmg",
    "order": 0,
    "field": "date",
    "options": {
      "start": "2019-05-03"
    }
  }
}

理想情况下,我希望结构如下。可能会有比这更多的层。

{
  "submit": {
    "order": 0,
    "field": "submit"
  },
  "hc3qu2nf4": {
    "label": "title",
    "parent": "",
    "order": 0,
    "field": "title",
    "options": {
      "Font Size": "25px",
      "Font Weight": "Bold",
      "Font Style": "Italic",
      "Color": "rgb(64, 128, 128)"
    }
  },
  "dhthivju9": {
    "label": "divider",
    "parent": "",
    "order": 1,
    "field": "divider",
    "options": {
      "height": "14px",
      "color": "#21ce09",
      "width": "50%"
    }
  },
  "4hhsi94n7": {
    "label": "column",
    "parent": "",
    "order": 2,
    "field": "column",
    "children": {
      "z5o9m7sgx": {
        "label": "",
        "order": 0,
        "field": "col",
        "children": {
          "sbf0bg1o7": {
            "label": "month",
            "parent": "z5o9m7sgx",
            "order": 0,
            "field": "month",
            "options": {
              "start": "2019-04"
            },
            "required": true
          }
        }
      },
      "85ugwci2c": {
        "label": "",
        "order": 1,
        "field": "col",
        "children": {
          "n5m9d84dg": {
            "label": "number",
            "parent": "85ugwci2c",
            "order": 0,
            "field": "number",
            "options": {
              "start": "5",
              "min": "5",
              "max": "10",
              "step": "2"
            }
          }
        }
      },
      "c3bwnyjmg": {
        "label": "",
        "order": 2,
        "children": {
          "krfxfnzsr": {
            "label": "date",
            "parent": "c3bwnyjmg",
            "order": 0,
            "field": "date",
            "options": {
              "start": "2019-05-03"
            }
          }
        }
      }
    }
  }
}

此外,如果我能保持每个项目的适当“顺序”,那么对它们进行适当的排序也可能很棒。


更新 我不喜欢输出结构。如果有人有更好的主意,请告诉我。

2 个答案:

答案 0 :(得分:0)

我找到了这个解决方案,但是它使用数组而不是对象(对于findreducesplicepush等),因此最终输出为并非完全符合您的要求,但您写的内容并非一成不变。

const data = {
  "submit": {
    "order": 0,
    "field": "submit"
  },
  "hc3qu2nf4": {
    "label": "title",
    "parent": "",
    "order": 0,
    "field": "title",
    "options": {
      "Font Size": "25px",
      "Font Weight": "Bold",
      "Font Style": "Italic",
      "Color": "rgb(64, 128, 128)"
    }
  },
  "dhthivju9": {
    "label": "divider",
    "parent": "",
    "order": 1,
    "field": "divider",
    "options": {
      "height": "14px",
      "color": "#21ce09",
      "width": "50%"
    }
  },
  "z5o9m7sgx": {
    "label": "",
    "parent": "4hhsi94n7",
    "order": 0,
    "field": "col"
  },
  "85ugwci2c": {
    "label": "",
    "parent": "4hhsi94n7",
    "order": 1,
    "field": "col"
  },
  "4hhsi94n7": {
    "label": "column",
    "parent": "",
    "order": 2,
    "field": "column"
  },
  "sbf0bg1o7": {
    "label": "month",
    "parent": "z5o9m7sgx",
    "order": 0,
    "field": "month",
    "options": {
      "start": "2019-04"
    },
    "required": true
  },
  "c3bwnyjmg": {
    "label": "",
    "parent": "4hhsi94n7",
    "order": 2
  },
  "n5m9d84dg": {
    "label": "number",
    "parent": "85ugwci2c",
    "order": 0,
    "field": "number",
    "options": {
      "start": "5",
      "min": "5",
      "max": "10",
      "step": "2"
    }
  },
  "krfxfnzsr": {
    "label": "date",
    "parent": "c3bwnyjmg",
    "order": 0,
    "field": "date",
    "options": {
      "start": "2019-05-03"
    }
  }
};

function sortArray(orderedArray, remainingArray) {
  if(remainingArray.length === 0) {
    return orderedArray;
  }
  else {
    remainingArray.forEach((remainingItem, index) => {
      if(orderedArray.find(x => x.identifier === remainingItem.parent)) {
        orderedArray.push(remainingItem);
        remainingArray.splice(index, 1);
      }
    });
    return sortArray(orderedArray, remainingArray);
  }
}

function insertNode(tree, node) {
  if(!tree) return;
  let item = tree.find(x => x.identifier === node.parent);
  if(item) {
    (item.children || (item.children = [])).push(node);
  } else {
    tree.forEach(x => {
      insertNode(x.children, node);
    });
  }
  return tree;
}

function createTree(data) {
  let tempArray = [];

  for (let key in data) {
    tempArray.push({ ...data[key], identifier: key });
  }
  
  tempArray = sortArray(tempArray.filter(x => !x.parent), tempArray.filter(x => x.parent));

  return tempArray.reduce((accumulator, currentValue) => {
    if(currentValue.parent === "") {
      accumulator.push(currentValue);
    } else {
      insertNode(accumulator, currentValue);
    }
    return accumulator;
  }, []);
}

console.log(createTree(data));

答案 1 :(得分:0)

下面的示例保留Object类型,而不是切换到Array。有关更多详细信息,请参见内联注释。基本概念是将树本身视为主分支,并在分支中进行迭代,直到找到父级以将子级重新定位到该子级。

//iterating through an object/array that's changing can cause unexpected results
//iterate through a duplicate that will not change
let tree = {
    "submit": {
      "order": 0,
      "field": "submit"
    },
    "hc3qu2nf4": {
      "label": "title",
      "parent": "",
      "order": 0,
      "field": "title",
      "options": {
        "Font Size": "25px",
        "Font Weight": "Bold",
        "Font Style": "Italic",
        "Color": "rgb(64, 128, 128)"
      }
    },
    "dhthivju9": {
      "label": "divider",
      "parent": "",
      "order": 1,
      "field": "divider",
      "options": {
        "height": "14px",
        "color": "#21ce09",
        "width": "50%"
      }
    },
    "z5o9m7sgx": {
      "label": "",
      "parent": "4hhsi94n7",
      "order": 0,
      "field": "col"
    },
    "85ugwci2c": {
      "label": "",
      "parent": "4hhsi94n7",
      "order": 1,
      "field": "col"
    },
    "4hhsi94n7": {
      "label": "column",
      "parent": "",
      "order": 2,
      "field": "column"
    },
    "sbf0bg1o7": {
      "label": "month",
      "parent": "z5o9m7sgx",
      "order": 0,
      "field": "month",
      "options": {
        "start": "2019-04"
      },
      "required": true
    },
    "c3bwnyjmg": {
      "label": "",
      "parent": "4hhsi94n7",
      "order": 2
    },
    "n5m9d84dg": {
      "label": "number",
      "parent": "85ugwci2c",
      "order": 0,
      "field": "number",
      "options": {
        "start": "5",
        "min": "5",
        "max": "10",
        "step": "2"
      }
    },
    "krfxfnzsr": {
      "label": "date",
      "parent": "c3bwnyjmg",
      "order": 0,
      "field": "date",
      "options": {
        "start": "2019-05-03"
      }
    }
  },
  tree2 = JSON.parse(JSON.stringify(tree)); //clone, not reference

console.log(tree === tree2); //test to confirm duplicate is not a reference

function reposition_child(parent, child, node, branch) {
  //check if the parent exists in this branch
  if (branch.hasOwnProperty(parent)) {
    let bp = branch[parent];
    //create children object as needed
    if (!bp.hasOwnProperty('children')) {
      bp.children = {};
    }
    //transplant the node as a child
    bp.children[child] = node;
    return true;
  } else {
    //iterate through the branches with children looking for the parent
    let found = false;
    for (let sub_branch in branch) {
      if (branch[sub_branch].hasOwnProperty('children')) {
        found = reposition_child(parent, child, node, branch[sub_branch].children);
      }
      //exit the loop once the parent is found
      if (found) {
        return true;
      }
    }
  }
  return false;
}

//iterate through the tree (duplicate fixed version)
for (let node in tree2) {
  let tn = tree2[node];
  //reposition nodes that have a non-empty parent
  if (tn.hasOwnProperty('parent') && tn.parent.length) {
    reposition_child(tn.parent, node, tn, tree);
    delete tree[node];
  }
}

//cleanup and output
delete tree2;
console.log(tree);