0

How can I clone a tree structure of an array of objects to another one, having different or less attibutes on the cloned one in TypeScript? For example on this:

[
   {
      "name":"root_1",
      "extradata":"whatever",
      "anArray":[
         { "id":1, "value":"test" },
         { "id":1, "value":"test" }
      ],
      "anObject":{ "id":1, "value":"test" },
      "children":[
         {
            "name":"child_1",
            "extradata":"whatever",
            "anArray":[],
            "anObject":{},
            "children":[
               {
                  "name":"root_1",
                  "extradata":"whatever",
                  "anArray":[],
                  "anObject":{},
                  "children":[]
               },
               {
                  "name":"root_1",
                  "extradata":"whatever",
                  "anArray":[],
                  "anObject":{},
                  "children":[]
               }
            ]
         }
      ]
   },
   {
      "name":"root_2",
      "extradata":"whatever",
      "anArray":[],
      "anObject":{},
      "children":[]
   }
]

I need a clone of it but instead of "name" the atribute "label", instad of "extradata" = "key" and without the "anArray" and "anObject". Like this:

[
   {
      "label":"root_1",
      "key":"whatever",
      "children":[
         {
            "label":"child_1",
            "key":"whatever",
            "children":[
               {
                  "label":"root_1",
                  "key":"whatever",
                  "children":[]
               },
               {
                  "label":"root_1",
                  "key":"whatever",
                  "children":[]
               }
            ]
         }
      ]
   },
   {
      "label":"root_2",
      "key":"whatever",
      "children":[]
   }
]

I need to clone it, to use it the data format for the primeng tree component.

I am trying with something like

this.myTree = tree.map((m, index) => Object.assign({}, {
                                label: m.name,
                                key: m.extradata,
                                children: m.children
                            }));

but the children are not following the same shape.

wilbi
  • 225
  • 2
  • 14

2 Answers2

1

I'm not sure if you can use 3rd party libraries - but have you heard of loadsh? There are numerous of examples on how to achieve that (:

first example

second example

If you cannot use it, I would just dive into the code and see how they implemented it (:

CloudBalancing
  • 1,461
  • 2
  • 11
  • 22
  • Thanks for answering. I 've seen it as a reference in similar questions, but no I cannot use it and from the examples I am not even sure if it works in a tree structure like this. – wilbi Oct 14 '20 at 18:33
1

I would do this by first defining interfaces for the input and output types in question:

interface InputTree {
  name: string,
  extradata: string,
  anArray: any[],
  anObject: object,
  children: InputTree[]
}

interface OutputTree {
  label: string,
  key: string,
  children: OutputTree[];
}

It looks like your input is an InputTree[] by that definition, and you want to produce an OutputTree[]. Trees are recursive, so you should write a treeMap() function that operates on an InputTree[] and then calls itself recursively on the children of each InputTree in the array. Like this:

const treeMap = (inputTree: InputTree[]): OutputTree[] => inputTree.map(t => ({
  label: t.name,
  key: t.extradata,
  children: treeMap(t.children)
}));

const actual = treeMap(tree);

You can verify that this produces the output you expect:

console.log(JSON.stringify(expected)===JSON.stringify(actual)); // true

Playground link to code

jcalz
  • 264,269
  • 27
  • 359
  • 360
  • I like your idea with interface and thanks for testing in typescriptlang. In fact the response is coming from ngrx/store and it's already mapped in a model. I didn't get how to use the recursive call to have the result I need though. Can you add it to the example please? – wilbi Oct 14 '20 at 18:29
  • Add it how? You can see that the implementation of `treeMap()` calls `treeMap()`, right? inside `inputTree.map(t => ({...})`, you need to set properties in the `...`. The `label` and `key` properties can just be copied from `t`, but the `children` property is an `InputTree[]` and you need it to be an `OutputTree[]`. Luckily we're writing a function that converts the former to the latter, so the `children` property becomes `treeMap(t.children)`. – jcalz Oct 14 '20 at 18:32
  • I had to try it now with the store data and it works fine. Thanks a lot man, really appreciated. I couldn't think such usage and I was losing time with the tree.map((m, index) => Object.assign({},. – wilbi Oct 14 '20 at 19:36
  • Do you have any idea how can I wrap the OutputTree with: { "data": [ ] } At the end it should be something like this to work: https://github.com/primefaces/primeng/blob/master/src/assets/showcase/data/files.json – wilbi Oct 14 '20 at 20:06
  • Like `const hmm = (inputTree: InputTree[]) => ({ data: treeMap(inputTree) }); hmm(tree);`? – jcalz Oct 15 '20 at 00:50
  • This hmm variable in your example replacing const treeMap = (inputTree: InputTree[]) somehow? Can you please add this wrapper { "data": [ ] } to your example in Playground link ? – wilbi Oct 15 '20 at 07:46
  • 1
    Like [this](https://tsplay.dev/GmZvam)? It's not replacing `treeMap`; it's using it. These follow-up questions in the comments seem to be outside the scope of the original question, so I'm not sure how much longer I can interact here. If you have a new problem you might want to start a new question to get more eyes on it. Good luck! – jcalz Oct 15 '20 at 13:58
  • Thanks again @jcalz for all your help! – wilbi Oct 15 '20 at 16:06