简体   繁体   中英

How do I flatten a (forest of) trees?

I have a forest of trees of arbitrary height, more or less like this:

let data = [
  { "id": 2, "name": "AAA", "parent_id": null, "short_name": "A" },
  {
    "id": 10, "name": "BBB", "parent_id": null, "short_name": "B", "children": [
      {
        "id": 3, "name": "CCC", "parent_id": 10, "short_name": "C", "children": [
          { "id": 6, "name": "DDD", "parent_id": 3, "short_name": "D" },
          { "id": 5, "name": "EEE", "parent_id": 3, "short_name": "E" }
        ]
      },
      {
        "id": 4, "name": "FFF", "parent_id": 10, "short_name": "F", "children": [
          { "id": 7, "name": "GGG", "parent_id": 4, "short_name": "G" },
          { "id": 8, "name": "HHH", "parent_id": 4, "short_name": "H" }
        ]
      }]
  }
];

And I'm trying to produce a representation of all the root-to-leaves paths, something like this

[
  [
    {
      "id": 2,
      "name": "AAA"
    }
  ],
  [
    {
      "id": 10,
      "name": "B"
    },
    {
      "id": 3,
      "name": "C"
    },
    {
      "id": 6,
      "name": "DDD"
    }
  ],
  [
    {
      "id": 10,
      "name": "B"
    },
    {
      "id": 3,
      "name": "C"
    },
    {
      "id": 5,
      "name": "EEE"
    }
  ],
  [
    {
      "id": 10,
      "name": "B"
    },
    {
      "id": 4,
      "name": "F"
    },
    {
      "id": 7,
      "name": "GGG"
    }
  ],
  [
    {
      "id": 10,
      "name": "B"
    },
    {
      "id": 4,
      "name": "F"
    },
    {
      "id": 8,
      "name": "HHH"
    }
  ]
]

So I wrote the following code:

function flattenTree(node, path = []) {
    if (node.children) {
        return node.children.map(child => flattenTree(child, [...path, child]));
  } else {
    let prefix = path.slice(0, path.length - 1).map(n => ({ id: n.id, name: n.short_name }));
    let last = path[path.length - 1];
    return [...prefix, { id: last.id, name: last.name } ];
  }
}

let paths = data.map(n => flattenTree(n, [n]));

but paths comes out with extra nesting, like this:

[
  [
    {
      "id": 2,
      "name": "AAA"
    }
  ],
  [
    [
      [
        {
          "id": 10,
          "name": "B"
        },
        {
          "id": 3,
          "name": "C"
        },
        {
          "id": 6,
          "name": "DDD"
        }
      ],
      [
        {
          "id": 10,
          "name": "B"
        },
        {
          "id": 3,
          "name": "C"
        },
        {
          "id": 5,
          "name": "EEE"
        }
      ]
    ],
    [
      [
        {
          "id": 10,
          "name": "B"
        },
        {
          "id": 4,
          "name": "F"
        },
        {
          "id": 7,
          "name": "GGG"
        }
      ],
      [
        {
          "id": 10,
          "name": "B"
        },
        {
          "id": 4,
          "name": "F"
        },
        {
          "id": 8,
          "name": "HHH"
        }
      ]
    ]
  ]
]

I lost count of the many ways in which I tried to fix this, but it does look like the algorithm should not produce the extra nesting -- or my eyes are just so crossed by now that I couldn't see my mistake if someone stuck their finger on it.

Can someone help? Feel free to peruse this JSFiddle https://jsfiddle.net/png7x9bh/66/

The extra nestings are created by map . map just wraps the results into an array and returns them, it doesn't care if it is called on child nodes or not. Use reduce and just concat (or push , whatever suits your performance) the results into the first level array directly:

 let data = [{"id":2,"name":"AAA","parent_id":null,"short_name":"A"},{"id":10,"name":"BBB","parent_id":null,"short_name":"B","children":[{"id":3,"name":"CCC","parent_id":10,"short_name":"C","children":[{"id":6,"name":"DDD","parent_id":3,"short_name":"D"},{"id":5,"name":"EEE","parent_id":3,"short_name":"E"}]},{"id":4,"name":"FFF","parent_id":10,"short_name":"F","children":[{"id":7,"name":"GGG","parent_id":4,"short_name":"G"},{"id":8,"name":"HHH","parent_id":4,"short_name":"H"}]}]}]; function flattenTree(node, path = []) { let pathCopy = Array.from(path); pathCopy.push({id: node.id, name: node.name}); if(node.children) { return node.children.reduce((acc, child) => acc.concat(flattenTree(child, pathCopy)), []); } return [pathCopy]; } let result = data.reduce((result, node) => result.concat(flattenTree(node)), []); console.log(JSON.stringify(result, null, 3)); 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM