how to create tree from flat array using in Angular - arrays

I need to show a tree from a flat array of data using Angular and I'm open to use any package to render the tree. as a user, I should be able to click on a node and find out details such as node ID and title. the tree should be expanded on load but users should be able to collapse parent nodes as they wish. my node data model looks like below:
export class Node {
nodeID: number;
title: string;
parentNodeID: number;
}
and my data looks like this:
public Nodes: Node[] = [
{
nodeID: 1;
title: parent1;
parentNodeID: null;
},
{
nodeID: 2;
title: child1;
parentNodeID: 1;
},
{
nodeID: 3;
title: child2;
parentNodeID: 1;
}
]

you need a recursive algorithm that looks loops through your flat array and maps parentNodeID to Node to generate tree structure and then use a tree component, for example angular-tree-component, to render your tree.
I made a demo on stackblitz. have a look and let me know if it helped.
https://stackblitz.com/github/ramin-ahmadi/Flat-Tree

There is plenty of package that could do the job, for example This one. I did not tried it but seems easy to use. If you can, change your keys in your array. Else, then just map your items into another array, something like:
const newArray = array.map(item =>({
id: item.nodeID,
name: item.title,
children: array.filter(el => el.parentNodeID === parentId), // Not sure about that, but this is the idea
})
);
newArray wil be the data provided to your three.

Related

Does a complex/deeply nested client-side state need to be normalised?

Background
I am building a single-page-application in React whose data will be retrieved from a relational database. Consider the following two tables:
menu
menu_items
A menu has many menu items. Menu items can be related to other menu items (represented in the database as an adjacency list). On the client, I'm representing it as a tree, i.e.:
{
"id": "menu",
"items": [
{
"id": "item-1",
"name": "Breakfast",
"children": []
},
{
"id": "item-2",
"name": "Lunch",
"children": [{ "id": "item-2-1", "children": [] }]
}
]
}
UI
A tree can get four levels deep and is typically much wider than it is tall. It is currently rendered recursively in the following way:
type Properties = {
items: {
id: string;
name: string;
children: Properties["items"];
}[];
};
const Items = ({ items }: Properties) => (
<ul>
{items.map((item) => (
<li key={item.id}>
{item.name}
<Items items={item.children} />
</li>
))}
</ul>
);
Problem
I have reached the stage where I want to update specific nodes in the tree. This operation seems complex, because it involves searching and replacing entire subtrees. Additionally, it will happen often, i.e. onChange, as a user updates item.name.
Although I don't use Redux, the following article explains it could be better to normalise nested client-side data to make operations like this easier: https://redux.js.org/usage/structuring-reducers/normalizing-state-shape.
Example
const menu = {
"id": "menu",
"itemMap": {
"item-1": { "parentId": null },
"item-2": { "parentId": null },
"item-2-1": { "parentId": "item-2" }
}
}
Question
Would I not have to denormalise/turn it back into a tree to render the UI? If yes, is there any point in my normalising the data?
I don't have a lot of experience with this and am struggling to find the right resources to answer the questions I have.
As with most engineering problems, there isn't a "correct" answer — rather it is a tradeoff: it depends on the expected use.
Your current approach optimizes for the maximum render performance at the cost of mutation performance. By using a tree structure, no transformation is needed at render time (just iteration) — however, arbitrary node lookups within the tree can't be done in constant time.
Another approach is to store the data as an associative array of nodes (Node ID ➡️ Node — e.g. Object/Map), which will optimize for arbitrary node lookup — and you can simply build the tree on every render by including each node's child IDs as part of its structure.
Here's an example of such a structure using the data that you provided:
TS Playground
<script src="https://cdn.jsdelivr.net/npm/#babel/standalone#7.20.15/babel.min.js"></script><script>Babel.registerPreset("tsx", {presets: [[Babel.availablePresets["typescript"], {allExtensions: true, isTSX: true}]]});</script>
<script type="text/babel" data-type="module" data-presets="tsx">
/** Any application-specific data type must satisfy this */
type ValidNode = {
/** Unique */
id: string;
/** Not allowed because of conflict */
children?: never;
};
// Your application-specific data type:
type Item = {
id: string;
name: string;
// etc.
};
type ListNode<T extends ValidNode> = T & {
/** List of child IDs */
children?: string[] | undefined;
};
// An object consisting of ID keys and ListNode values:
type ListNodeMap<T extends ValidNode> = Partial<Record<string, ListNode<T>>>;
const nodeMap: ListNodeMap<Item> = {
"menu": {
id: "menu",
name: "Menu",
children: ["item-1", "item-2"],
},
"item-1": {
id: "item-1",
name: "Breakfast",
},
"item-2": {
id: "item-2",
name: "Lunch",
children: ["item-2-1"],
},
"item-2-1": {
id: "item-2-1",
name: "Irresistibly healthy",
},
};
// A type-safe getter function which throws on bad IDs:
function getNode (
nodeMap: ListNodeMap<Item>,
id: string,
): ListNode<Item> {
const node = nodeMap[id];
if (!node) throw new Error(`Node ID ${JSON.stringify(id)} not found`);
return node;
}
/** The "linked" version of a ListNode */
type TreeNode<T extends ValidNode> = T & {
children?: T[] | undefined;
};
// Note: This uses recursion because it's for tree structures.
// Calling with list nodes having cyclic reference IDs will create an infinite loop.
function createTree (
nodeMap: ListNodeMap<Item>,
id: string,
): TreeNode<Item> {
const node = getNode(nodeMap, id);
return {
...node,
children: node.children?.map(id => createTree(nodeMap, id)),
};
}
console.log("node map:", nodeMap);
console.log("tree:", createTree(nodeMap, "menu"));
</script>
You didn't show how you receive the data, but if your API returns nodes with parent IDs instead of child IDs, then you can simply lookup each parent when acquiring new children and insert the child IDs at the time of acquisition — using an intermediate mapping structure if needed... that's tangential to the asked question.
You also didn't show how you plan to update node names, so I've excluded that part in the linked playground below, but here's an otherwise complete example of the code above with state and a reducer for updating an arbitrary node: Full example with state and reducer in TS Playground

TanStack Table 8 : access nested array in data model

I'm looking for a way to access a nested array in my data structure.
My objects in my data array look like this :
{
name: name,
logo: url,
categories: [
{
name: Entertainment,
slug: "entertainment
},
{
name: Kids,
slug: kids
},
]
}
In the Docs I states that I need to use columnHelper.accessor to extract primitive values for each item in your data array.
My question is : how can I configure my accessor on "categories" to display both of them in my cell (using map I guess) ?
First, you don't need to use columnHelper; you can, for convenience (well, type safety, mostly).
You can do something like this:
cell: info => {
const value = info.getValue() as YourCategoryType[]; //casting may not be required here
return <>{value.map(v => <p key={v.slug}>{v.name}</p>)}</>
},

React: How to traverse through all children nodes in the tree view structure?

I am working with react-expandable-treeview structure for displaying a family tree data. With the following code, I am traversing through the items object with family tree data, storing it into const data and then recursively passing into TreeView like it is indicated in documentation of react-expandable-treeview:
const items = props.item;
var x = 0;
const data = [{
id: x,
label: items.first_name, // parent #1
children: items.children.map( (child, idx) => {
id: x= ++idx,
label: child.first_name, // child of parent #1
children: child.children.map( (child_child) => ({
id: x = ++idx,
label: child_child.first_name,
children: child_child.children
? child_child.children.map( (child_child_child) => ({
id: x = ++idx,
label: child_child_child.first_name,
children: child_child_child.children
}))
: null
}))
}))
}]
However, with the algorithm above, I was able to only get the root node, its children, and two more generations of the family. This is a screenshot of how the structure looks like, it shows the amount of the nodes and their relationships correctly, but their indexes and first names are not displayed correctly.
I can't figure out how to perform the last mapping function, so that
id: x = ++idx,
label: the_rest_of_children.first_name,
children: the_rest_of_children.children
worked for the rest of children and expanded all nodes children until the very last node. How can I fix my code to get the correct output?
Use recursion
let index = 0
function visit(node) {
return {
label: node.first_name,
id: index++,
// pass `visit` function as an argument
children: node.children.map(visit)
}
}
const data = visit(props.items)
Recursion isn't the only way to traverse tree-like structures. If your tree is extremely complex, it'd be safer to implement iterative traversal algorithm:
If the recursive implementation is so simple, why bother with an iterative one? Of course, to avoid stack overflow. Most runtime engines/compilers set a limit on how many nested calls a program can make. If the height of the tree is larger than this limit, the program will crash with a stack overflow error. ...
But those cases aren't common in web development.

How to add/remove nodes to React Tree View

I want to create a tree that allows the user to be able to add/remove nodes by clicking on the node that the user would like to add children to/remove the node they click on. I am using the react-expendable-treeview package as I like how it looks visually. Javascript functionality can be found under src/lib/components/
The github repo can be found here: https://github.com/fosco/react-expandable-treeview and the tree looks like so: https://imgur.com/7Y5xcIj
The data that is passed into the TreeView component is predefined in a javascript file like so:
const testData = [
{
id: 0,
name: "Felidae",
children: [
{
id: 1,
name: "Pantherinae",
children: [
{
id: 2,
name: "Neofelis",
},
{
id: 3,
name: "Panthera",
}
]
},
{
I'm wondering how I can set this up so I am able to add/remove children. I am aware that there are many other react tree packages such as react-d3-tree. However, I visually like how this one looks. If anyone knows how I can modify this package or change the theme of react-d3-tree or another package to look like this, please let me know.

How can I get an item in the redux store by a key?

Suppose I have a reducer defined which returns an array of objects which contain keys like an id or something. What is the a redux way of getting /finding a certain object with a certain id in the array. The array itself can contain several arrays:
{ items:[id:1,...],cases:{...}}
What is the redux way to go to find a record/ node by id?
The perfect redux way to store such a data would be to store them byId and allIds in an object in reducer.
In your case it would be:
{
items: {
byId : {
item1: {
id : 'item1',
details: {}
},
item2: {
id : 'item2',
details: {}
}
},
allIds: [ 'item1', 'item2' ],
},
cases: {
byId : {
case1: {
id : 'case1',
details: {}
},
case2: {
id : 'case2',
details: {}
}
},
allIds: [ 'case1', 'case2' ],
},
}
Ref: http://redux.js.org/docs/recipes/reducers/NormalizingStateShape.html
This helps in keeping state normalized for both maintaining as well as using data.
This way makes it easier for iterating through all the array and render it or if we need to get any object just by it's id, then it'll be an O(1) operation, instead of iterating every time in complete array.
I'd use a library like lodash:
var fred = _.find(users, function(user) { return user.id === 1001; });
fiddle
It might be worth noting that it is seen as good practice to 'prefer objects over arrays' in the store (especially for large state trees); in this case you'd store your items in an object with (say) id as the key:
{
'1000': { name: 'apple', price: 10 },
'1001': { name: 'banana', price: 40 },
'1002': { name: 'pear', price: 50 },
}
This makes selection easier, however you have to arrange the shape of the state when loading.
there is no special way of doing this with redux. This is a plain JS task. I suppose you use react as well:
function mapStoreToProps(store) {
function findMyInterestingThingy(result, key) {
// assign anything you want to result
return result;
}
return {
myInterestingThingy: Object.keys(store).reduce(findMyInterestingThingy, {})
// you dont really need to use reduce. you can have any logic you want
};
}
export default connect(mapStoreToProps)(MyComponent)
regards

Resources