1

I need to make a tree Array to use in primeng tree component, but I receive from back end a flat array.

Example that I receive:

{name: 'teste', previousName: 'fathername', showOrder: '1', preferredOrder: '2'},
{name: 'teste 2', previousName: 'fathername', showOrder: '1', preferredOrder: '2'},
{name: 'teste 3', previousName: 'teste', showOrder: '1', preferredOrder: '2'},
{name: 'teste 4', previousName: 'teste', showOrder: '1', preferredOrder: '2'},
{name: 'teste 5', previousName: 'teste 3', showOrder: '1', preferredOrder: '2'},
{name: 'teste 6', previousName: 'teste 5', showOrder: '1', preferredOrder: '2'},
]

and I need transform in:

[
{label: 'name', data: 'object origial', draggable: true, droppable: true, children: []}
]

In first of all, i try make the original array in objects that I need with this function

makeTreeNode(array) {
  let arrTreeNode = []
  let treeNodeObj;
  array.filter(element => {
   treeNodeObj = new tree() //tree is a class with the properties that I desire
   treeNodeObj.label = element.name
   treeNodeObj.data = element
   treeNodeObj.draggable = true
   treeNodeObj.droppable = true
   arrTreeNode.push(treeNodeObj)
})

and this works, but I don't know how I can Read this new array to make de object that have the previous name equal the name and put in the children.

Anyone can help me please???

6
  • Please provide a self-contained minimal reproducible example suitable for pasting into a standalone IDE to reproduce your issue. If there's code needed to make this run that isn't shown here (like tree) then either include that code, or (even better) remove the example's dependence on it. If you make it easier for others to run your code, then you make it easier for them to modify it, test it, and answer. Good luck! Commented Nov 21, 2022 at 20:54
  • Does this approach meet your needs? Note that your example data didn't have a single root node (you make mention of "fathername" but there is no element with that name) so my function returns an array of root nodes. And I gave a definition of Tree that was easy to use. If this works for you I could write up an answer explaining; if not, what am I missing? (Pls mention @jcalz in a reply to notify me) Commented Nov 21, 2022 at 21:06
  • @jcalz I'm going to test what you've done here and if it works I'll let you know here so you can create the answer, looking quickly it seems that it does what I need, I'll just put it in the code and test it. If it doesn't work, I'll bring more details than I need, but basically the function I created creates the nodes in the pattern that primeng needs. The nodes I left as 'fathername' really don't have a father and would stand alone. I took the tests here and I believe that was exactly what I needed ... continue... Commented Nov 22, 2022 at 1:34
  • if you can explain it to me in as much detail as possible, sending me the documentation for me to understand and study more, I would be very grateful. Also, how could I do the reverse process, because when sending it back to the api I'll need to send the array according to the Data interface you defined, would that be simple? @jcalz Commented Nov 22, 2022 at 1:34
  • "how could I do the reverse process" is a follow-up question and out of scope for the question as asked. I would do it this way but if you need it explained or need a different outcome you should open a new question post for it. When you are ready to tell me whether or not I should write up an answer, let me know via mentioning @jcalz. Thanks! Commented Nov 22, 2022 at 1:51

1 Answer 1

1

For concreteness, I will define your array like this

const arr: Data[] = [
  { name: 'teste', previousName: 'fathername', showOrder: '1', preferredOrder: '2' },
  { name: 'teste 2', previousName: 'fathername', showOrder: '1', preferredOrder: '2' },
  { name: 'teste 3', previousName: 'teste', showOrder: '1', preferredOrder: '2' },
  { name: 'teste 4', previousName: 'teste', showOrder: '1', preferredOrder: '2' },
  { name: 'teste 5', previousName: 'teste 3', showOrder: '1', preferredOrder: '2' },
  { name: 'teste 6', previousName: 'teste 5', showOrder: '1', preferredOrder: '2' },
]

where Data is the following interface:

interface Data {
  name: string,
  previousName: string,
  showOrder: string;
  preferredOrder: string
}

And the goal is to implement the makeTreeNode() function with the following call signature:

declare function makeTreeNode(array: Data[]): Tree<Data>[];

where Tree is a generic class like

class Tree<T> {
  constructor(
    public label: string,
    public data: T,
    public children: Tree<T>[] = [],
    public draggable = true,
    public droppable = true
  ) { }
}

Here's one possible approach:

function makeTreeNode(array: Data[]) {

  // keep a mapping from node name to node:
  const nodeMap: Record<string, Tree<Data>> = {};
  array.forEach(element => 
    nodeMap[element.name] = new Tree(element.name, element)
  );
  
  // populate the children
  array.forEach(element =>
    nodeMap[element.previousName]?.children.push(nodeMap[element.name])
  );

  // return only the nodes without a parent
  return Object.values(nodeMap).filter(n => 
    !(n.data.previousName in nodeMap)
  );
  
}

There are three steps:

  • For each Data element, create a corresponding Tree<Data> node (with an empty children array) and put it in nodeMap at the key corresponding to the element name. This lets us easily look up nodes by name later.

  • For each Data element, find the corresponding node in nodeMap, and push it onto the children array of the node corresponding to the name of its parent. When this is done, all the nodes' children arrays will be fully populated.

  • Filter the array of Tree<Data> elements in the values of nodeMap so that we only keep those elements that have no parent element in nodeMap. This is an array of the root elements, and it is this array that we return.


Let's test it out:

function displayTreeNodes(array: Tree<Data>[]): string {
  return "[" + array.map(t => t.label + ": " + displayTreeNodes(t.children)).join(", ") + "]"
}
console.log(displayTreeNodes(rootNodes));
// "[teste: [teste 3: [teste 5: [teste 6: []]], teste 4: []], teste 2: []]" 

Looks good, we now have a tree structure with two root nodes.


Note that the implementation of makeTreeNode() could be improved to avoid an extra loop through the array, by collapsing the last two steps into one:

// populate children and return array
const ret: Tree<Data>[] = [];
array.forEach(element =>
  (nodeMap[element.previousName]?.children ?? ret).push(nodeMap[element.name])
)
return ret;

But I presented the original version since it demonstrates the concepts more clearly.

Playground link to code

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.