I have a huge json file with following structure:
name: 'xxx',
worth: [123, 456, 789]
children: [
{name: 'xxx',
worth: [987, 654, 321],
children: [
{name: 'xxx',
worth: [213, 546, 879],
children: []}
},
{name: 'xxx',
worth: [987, 654, 321],
children: [
name: 'xxx',
worth: [213, 546, 879],
children: []
}],
]
The number of children can get up to 10 layer deep.
I created an angular component that displays name and worth and takes children as an Input to call itself again with the children's name and worth but I get maximum stack size errors with this approach.
How to write a function that can loop through this json until children array is empty and display the name and worth of all of them on the way?
This screams recursion but I cant manage...
Presuming your root object represents a single child, your code would look like this:
const child = {
name: "xxx1",
worth: [123, 456, 789],
children: [
{
name: "xxx2",
worth: [987, 654, 321],
children: [
{
name: "xxx3",
worth: [213, 546, 879],
children: []
}
]
},
{
name: "xxx4",
worth: [987, 654, 321],
children: [
{
name: "xxx5",
worth: [213, 546, 879],
children: []
}
]
}
]
};
const sum = (arr) => arr.reduce((a, b) => a + b, 0);
function process_child(child, result) {
const { name, worth, children } = child;
result.push({ name, worth: sum(worth) });
for (const c of children) {
process_child(c, result);
}
return result;
}
const children = process_child(child, []);
console.table(children);
Here, process_child is a function that takes in a single child and a list of results (initially empty). It populates the list of results while going through all the nested children. The end result is an array of objects with the structure {name, worth}, which you can then consume in whatever way you wish.
This is not an Angular-specific problem, and in fact you will just want your Angular component to be a passive consumer of the data returned by this function.
Here's a working execution on CodeSandbox where the result is simply displayed on the page in JSON form.
Related
I'm working with a tree nodes and I want to create a function to find an item by its ID.
What is the best and optimal solution to get it?
I think it will be a recursive function, but I'm not sure and I want a help please to choose what I will use to resolve this issue :)
This is my example:
const treeData = [
{
title: 'parent 1',
key: 11,
children: [
{
title: 'parent 1-0',
key: 12,
children: [
{
title: 'leaf',
key: 13,
children: [
{
title: 'leaf111',
key: 14,
},
{
title: 'leaf',
key: 15,
},
],
},
{
title: 'leaf666',
key:88,
},
],
},
{
title: 'parent 1-1',
key: 55,
children: [
{
title: (
<span
style={{
color: '#1890ff',
}}
>
sss
</span>
),
key: '0-0-1-0',
},
],
},
],
},
];
Input : 14
Output : {title: 'leaf111',key: 14},
We can create a rerusive function that:
Loops though each object in the array and
Returns the object if id matches
Calls itself with the objects children
const treeData = [{title: 'parent 1', key: 11, children: [{title: 'parent 1-0', key: 12, children: [{title: 'leaf', key: 13, children: [{title: 'leaf111', key: 14, }, {title: 'leaf', key: 15, }, ], }, {title: 'leaf666', key:88, }, ], }, {title: 'parent 1-1', key: 55, children: [{title: '(<span style={{color: \'#1890ff\', }} > sss </span> )', key: '0-0-1-0', }, ], }, ], }, ];
const findById = (e, id) => {
for (let o of e) {
return (o.key == id) ? o : findById(o.children, id);
}
}
const res = findById(treeData, 14);
console.log(res);
Output:
{
"title": "leaf111",
"key": 14
}
You can use a tree walker, which traverses the tree and calls a provided function upon visiting each node. The provided function could check to see if the node matches the provided ID.
An example:
function getNodeById(id) {
let matchingNode;
walk(tree, node => {
if(node.key === 14) {
matchingNode = node;
}
});
return matchingNode;
}
A tree walker can be implemented using recursion, as you mention. In a preorder operation, which starts at the topmost node, the walker takes the tree (the root node) and on each invocation:
calls the callback function with the current node
for each child node, calls itself with the child node and callback
function walker(node, cb) {
cb(node);
if (Array.isArray(node.children)) {
node.children.forEach(child => walker(child, cb));
}
}
For use with React, you can implement your own walking using React.Children.forEach, or you may prefer try a library like https://github.com/FormidableLabs/react-ssr-prepass.
i have to provide type for array of objects using react and typescript?
below is the code,
const SomeComponent = (item: string, children: any) => {
//some logic
}
here as you see for children i have use any type instead i want to use type of its own. when i log children in console its like below an array of objects
[
{
id: '1',
children: [],
},
{
id: '2',
children: [
{
id: '21',
name: 'children1',
},
],
},
]
What should be the type of children instead of any. i am new to using typescript. could someone help me with this. thanks.
If you have defined class or interface Child, you can use
children: Child[]
In general, array of object is noted as object[]
Assuming that children and top level array is not recursive data structure you can use this type:
type Item = {
id: `${number}`;
children: Child[]
}
type Child = {
id: `${number}`;
name: `children${number}`;
}
const arr: Item[] = [
{
id: '1',
children: [],
},
{
id: '2',
children: [
{
id: '21',
name: 'children1',
},
],
},
]
This syntax/type - `${number}` means that it is a string but contains only numbers
I'm using Apollo Client 3 in a react project.
I have a data structure like this:
ROOT_QUERY
getCollection {
__typename: 'Collection',
_id: '123'
tagColorMap: [
{__typename: 'Tag',
name: 'tag1',
color: "#673ab7",
count: 3},
{__typename: 'Tag',
name: 'tag2',
color: '#f44336',
count: 1},
...
]
entries: [
{
__typename: 'Entry',
_id: 'asd'
tags: [tag1, tag2, tag3]
},
{
__typename: 'Entry',
_id: 'qwe'
tags: [tag2, tag3]
},
...
}
}
the data are normalised in the cache.
ROOT_QUERY
getCollection{
"__ref": "Collection:123"
}
Collection:123{
_id: '123'
tagColorMap: [
{__typename: "Tag",
name: "tag1",
color: "#673ab7",
count: 3},
{__typename: "Tag",
name: "tag2",
color: "#f44336",
count: 1},
...
]
entries: [
{
__ref: "Entry:asd"
},
{
__ref: "Entry:qwe"
},
...
]
Entry:asd {
_id: 'asd'
tags: ['tag1', 'tag2', 'tag3']
},
Entry:qwe {
_id: 'qwe'
tags: ['tag2', 'tag3']
},
I performed a mutation, which renames one of the tag, say 'tag1' -> 'tag11', which returns the new tagColorMap;
now I want to change all 'tag1' into 'tag11' in the cache.
I have gone through the official doc and googled it for a while, but still can't find a way to do this.
refetching won't work because the time between the mutation is done and the refetch is done, all the entries that still have tag 'tag1' don't have a corresponding colour in the colour map, it will fallback to default colour, then back to the original colour after the refetch is done.
another way might be that to let the server return the entire collection after the mutation, which is quite a lot of data
so that's why I would like to rename all 'tag1' into 'tag11' in all 'entry" objects directly in cache, but I couldn't find a way to do this... Could anyone help me with this?
thank you very much in advance!
I'm using ant's table and trying to setup dynamic columns.
What I need is a table that shows a list of users with their performance for each of the classes as in the example below.
Details: I have a grouped column Performance that can have different sub-columns (current example shows columns science and physics). I'm calling renderContent() which sets up an object that has property children. I found this "solution" from ant's example here. The problem is that ant's example outputs children prop type string, while my function outputs prop type array. Which results in the error.
Here is a link to sandbox:
https://codesandbox.io/embed/ecstatic-cookies-4nvci?fontsize=14
Note: if you uncomment children array in columns [line 46-59], you will see what my expected result should be.
The render method shouldn't return the object with children array. To use the render method, you would have to return a valid React component (or simply HTML tag ---like span).
However in your case, I prefer we extract subjects before passing it into the table and then generate children array dynamically. Something like below:
const renderContent = (value, row, index) => {
return setupPerformance(value)
};
const setupPerformance = performance => {
return performance.map(p => {
const { id, title, percentage } = p;
return <span>{percentage}%</span>
});
};
const data = [
{
key: 0,
firstName: "John",
lastName: "Smith",
performance: [
{
id: 1,
title: "science",
percentage: 75
},
{
id: 2,
title: "physics",
percentage: 36
}
]
},
{
key: 1,
firstName: "Ann",
lastName: "Smith",
performance: [
{
id: 1,
title: "science",
percentage: 68,
timeSpent: 50,
completionDate: "2019-02-07"
},
{
id: 2,
title: "physics",
percentage: 100
}
]
}
];
let subjects = data[0].performance
const columns = [
{
title: "Full Name",
children: [
{
title: "firstName",
dataIndex: "firstName",
key: "firstName"
},
{
title: "lastName",
dataIndex: "lastName",
key: "lastName"
}
]
},
{
title: "Performance",
dataIndex: "performance",
children:
subjects.map(e => {
return {
title: e.title,
dataIndex: "performance["+(e.id-1)+"].percentage",
key: "key-"+e.id,
render: value => <span>{value}%</span>
}
})
}
];
Because of the solution in answer from Mobeen does not work anymore, I have tried to solve this.
I have extended the render method for the children columns of performance column:
...
{
title: "Performance",
dataIndex: "performance",
children: subjects.map((assessment) => {
const { title, id } = assessment;
return {
title,
dataIndex: "performance",
key: id,
render: (values) =>
values.map((value, index) => {
let ret;
if (index === id - 1) ret = values[index].percentage + "%";
return ret;
})
};
})
}
...
It returns only the percentage value of the subject with the corresponding id.
It is not very clean, but it works.
Check the solution in sandbox: https://codesandbox.io/s/prod-lake-7n6zgj
Let's say I have the data source as following:
var data = [
{
id: 1,
name: 'name1',
parentId: null,
children: [
{
id: 2,
name: 'name2',
parentId: 1
}
]
},
{
id: 3,
name: 'name3',
parentId: null,
children: [
{
id: 4,
name: 'name4',
parentId: 3
}
]
}
]
And the code snippets like following:
var basic_grid_store = Ext.create('Ext.data.TreeStore', {
storeId: 'basic_grid_store',
model: 'TestModel',
root: {
children: []
}
});
console.log(data);
// the data structure is correct at this time
basic_grid_store.setRootNode({children: data);
console.log(data);
// the data structure is incorrect at this time, in which the `children` attribute for each item was gone.
I could not find any documentation for this, can someone tell why TreeStore modified my data source since it should not happen?
Yeah, it does change the original array. I cannot answer why this behavior, you would need to ask Ext architects/developers, but what you can try is:
basic_grid_store.setRootNode({children:Ext.clone(data)});