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???
CodePudding user response:
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 correspondingTree<Data>
node (with an emptychildren
array) and put it innodeMap
at the key corresponding to the elementname
. This lets us easily look up nodes by name later.For each
Data
element, find the corresponding node innodeMap
, and push it onto thechildren
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 ofnodeMap
so that we only keep those elements that have no parent element innodeMap
. 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.