I am working on a angular application. I have one array and in it I have one flag "checked". I need to do some manipulation
on the basis of this flag. My sample data is as follows:
const data = [{
"checked": true,
"children": [{
"checked": true,
"data": {
"name": "myName"
},
"parent": {
"data": {
"name": "myName2"
},
"parent": {
"data": {
"name": "myName3"
}
}
}
}, {
"checked": true,
"data": {
"name": "myNamePart2"
},
"parent": {
"data": {
"name": "myName2"
},
"parent": {
"data": {
"name": "myName3"
}
}
}
}
]
}, {
"checked": false,
"children": [{
"checked": true,
"data": {
"name": "myName4"
},
"parent": {
"data": {
"name": "myName5"
},
"parent": {
"data": {
"name": "myName6"
}
}
}
}
]
}, {
"checked": true,
"children": [{
"checked": true,
"data": {
"name": "myName7"
},
"parent": {
"data": {
"name": "myName8"
},
"parent": {
"data": {
"name": "myName9"
}
}
}
}
]
},
{
"checked": true,
"data": {
"name": "myName10"
},
"parent": {
"data": {
"name": "myName11"
},
"parent": {
"data": {
"name": "myName12"
}
}
}
},
{
"checked": false,
"data": {
"name": "myName13"
},
"parent": {
"data": {
"name": "myName14"
},
"parent": {
"data": {
"name": "myName15"
}
}
}
}
];
In this array, if for any index if "checked" is true, then I want to check the "checked" flag for each children. If for any children, if "checked" flag is
true, then I want to have data from parent name to children name separated by "/" in an array. For above data my final array will have
result = ["myName3/myName2/myName","myName3/myName2/myNamePart2","myName9/myName8/myName7","myName12/myName11/myName10"]
How can I do that?
I've found a pretty short recursive solution to your problem:
const data = [{
"checked": true,
"children": [{
"checked": true,
"data": {
"name": "myName"
},
"parent": {
"data": {
"name": "myName2"
},
"parent": {
"data": {
"name": "myName3"
}
}
}
}, {
"checked": true,
"data": {
"name": "myNamePart2"
},
"parent": {
"data": {
"name": "myName2"
},
"parent": {
"data": {
"name": "myName3"
}
}
}
}
]
}, {
"checked": false,
"children": [{
"checked": true,
"data": {
"name": "myName4"
},
"parent": {
"data": {
"name": "myName5"
},
"parent": {
"data": {
"name": "myName6"
}
}
}
}
]
}, {
"checked": true,
"children": [{
"checked": true,
"data": {
"name": "myName7"
},
"parent": {
"data": {
"name": "myName8"
},
"parent": {
"data": {
"name": "myName9"
}
}
}
}
]
}, {
"checked": true,
"data": {
"name": "myName10"
},
"parent": {
"data": {
"name": "myName11"
},
"parent": {
"data": {
"name": "myName12"
}
}
}
}, {
"checked": false,
"data": {
"name": "myName13"
},
"parent": {
"data": {
"name": "myName14"
},
"parent": {
"data": {
"name": "myName15"
}
}
}
}
];
const compute = data =>
data
.flatMap(x => x.checked && x.children)
.concat(data)
.filter(x => x && x.checked && x.data)
.map(x => createPath(x))
.map(x => x.join("/"));
const createPath = (node, currentPath = []) =>
node.parent
? createPath(node.parent, [node.data.name, ...currentPath])
: [node.data.name, ...currentPath];
console.log(compute(data));
Related
So I have an array of objects which have all nested children property. It is in front-end a treeview, which should expand the nodes until selected ones, for each id in a list. To be able to do this, I have to get all the parents ids for each selected id from the list.
For example, my list of checkedKeys ids:
[16787217, 16787245, 16787266, 16787270, 16787272, 16787265, 16787264]
All the checked items represents the ids from the list object:
My object list looks like this:
[
{
"id": 11,
"name": "Forecast source",
"value": null,
"children": []
},
{
"id": 2,
"name": "Item Type",
"value": null,
"children": []
},
{
"id": 16787217,
"name": "Item#Cust",
"value": null,
"children": [
{
"id": 16787230,
"name": "Customer",
"value": null,
"children": [
{
"id": 16787291,
"name": "Commercial Network",
"value": null,
"children": []
},
{
"id": 16787296,
"name": "Distribution Site",
"value": null,
"children": []
},
{
"id": 16787265,
"name": "Site",
"value": null,
"children": []
}
]
},
{
"id": 16787254,
"name": "Item",
"value": null,
"children": [
{
"id": 16787294,
"name": "ABC (Regular)",
"value": null,
"children": []
},
{
"id": 16787273,
"name": "ABC (U)",
"value": null,
"children": []
},
{
"id": 16787278,
"name": "ABC (€)",
"value": null,
"children": []
},
{
"id": 16787290,
"name": "Class",
"value": null,
"children": []
},
{
"id": 16787260,
"name": "Family",
"value": null,
"children": [
{
"id": 16787264,
"name": "Product line",
"value": null,
"children": []
}
]
},
{
"id": 16787263,
"name": "Flavour",
"value": null,
"children": []
},
{
"id": 16787262,
"name": "Format",
"value": null,
"children": []
},
{
"id": 16787261,
"name": "Group 1",
"value": null,
"children": []
},
{
"id": 16787292,
"name": "ProdGroup",
"value": null,
"children": []
},
{
"id": 16787293,
"name": "Recipe",
"value": null,
"children": []
},
{
"id": 16787288,
"name": "Sale status",
"value": null,
"children": []
}
]
},
{
"id": 16787245,
"name": "Item#Site",
"value": null,
"children": [
{
"id": 16787266,
"name": "Family#Warehouse",
"value": null,
"children": []
}
]
}
]
},
{
"id": 3,
"name": "Lead Time",
"value": null,
"children": []
},
{
"id": 5,
"name": "Levels",
"value": null,
"children": []
},
{
"id": 16787268,
"name": "N1",
"value": null,
"children": [
{
"id": 16787269,
"name": "N2",
"value": null,
"children": [
{
"id": 16787270,
"name": "N3",
"value": null,
"children": [
{
"id": 16787271,
"name": "N4",
"value": null,
"children": [
{
"id": 16787272,
"name": "N5",
"value": null,
"children": [
{
"id": 33564497,
"name": "N6",
"value": null,
"children": [
{
"id": 33564498,
"name": "N7",
"value": null,
"children": [
{
"id": 33564499,
"name": "N8",
"value": null,
"children": [
{
"id": 33564500,
"name": "N9",
"value": null,
"children": []
}
]
}
]
}
]
}
]
}
]
}
]
}
]
}
]
},
{
"id": 16787286,
"name": "Op set",
"value": null,
"children": []
}
]
So my problem is that I can't figure out how to get all parents nodes ids recursively until the root level and push them into the expandedKeys array.
I tried to implement a function like this:
this.list.forEach(el => {
this.setExpandedNodes(el);
});
setExpandedNodes(node: PropertyNode) {
if (node.children) {
node.children.forEach(chld => {
this.checkedKeys.forEach(key => {
if (chld.id === key) {
this.expandedKeys.push(node.id);
} else if (chld.children) {
chld.children.forEach(grChld => {
this.setExpandedNodes(grChld);
});
}
});
});
}
}
But I can't figure out how to get all parent ids starting from each selected id until the root level. Anyone have an idea?
Thanks to this answer I managed to do it.
getPath(model, id) {
let path,
item = model.id ;
if (!model || typeof model !== 'object') {
return;
}
if (model.id === id) {
return [item];
}
(model.children || []).some(child => (path = this.getPath(child, id)));
return path && [item, ...path];
}
setExpandedKeys() {
if (this.checkedKeys && this.checkedKeys.length > 0) {
this.checkedKeys.forEach(k => {
this.list.forEach(
mt => {
const result = this.getPath(mt, k);
if (result) {
this.expandedKeys.push(...result);
}
}
);
});
}
}
this.setExpandedKeys();
I have now all the parents ids until the root.
A function that returns an array of parent objects may look like this:
Using tree-model-js:
const getParentsById = (id, data) => {
var TreeModel = require('tree-model')
const tree = new TreeModel()
let path
for (let item of data) {
const root = tree.parse(item)
root.walk(function (node) {
// Halt the traversal by returning false
if (node.model.id === id) {
path = node.getPath()
return false;
}
});
}
path.pop()
return path.map(item => item.model)
}
Without using tree-model-js:
const getParentsById = (id, data) => {
const isFoundChild = (id, data, parents) => {
if (data.find(item => item.id == id)) {
return true;
}
else {
for (let item of data) {
if (item.children.length)
if (isFoundChild(id, item.children)) {
parents.push(item);
return true;
}
}
return false;
}
}
const parents = [];
if (data.find(item => item.id == id))
return [];
else {
for (let item of data) {
if (item.children.length)
if (isFoundChild(id, item.children, parents)) {
parents.push(item);
return parents;
}
}
}
}
Next, it is enough to apply map() to the result:
let parentsId = getParentsById(16787296, data).map(item => item.id)
I am working on an angular application. My data is as follows.
const data = [{
"checked": true,
"children": [{
"checked": true,
"data": {
"name": "myName"
},
"parent": {
"checked":true,
"data": {
"name": "myName2"
},
"parent": {
"checked":true,
"data": {
"name": "myName3"
}
}
}
}, {
"checked": true,
"data": {
"name": "myNamePart2"
},
"parent": {
"data": {
"name": "myName2"
},
"parent": {
"data": {
"name": "myName3"
}
}
}
}
]
}, {
"checked": false,
"children": [{
"checked": true,
"data": {
"name": "myName4"
},
"parent": {
"data": {
"name": "myName5"
},
"parent": {
"data": {
"name": "myName6"
}
}
}
}
]
}, {
"checked": true,
"children": [{
"checked": true,
"data": {
"name": "myName7"
},
"parent": {
"data": {
"name": "myName8"
},
"parent": {
"data": {
"name": "myName9"
}
}
}
}
]
},
{
"checked": true,
"data": {
"name": "myName10"
},
"parent": {
"data": {
"name": "myName11"
},
"parent": {
"data": {
"name": "myName12"
}
}
}
},
{
"checked": false,
"data": {
"name": "myName13"
},
"parent": {
"data": {
"name": "myName14"
},
"parent": {
"data": {
"name": "myName15"
}
}
}
}
];
My expected output is :
In this array, if for any index if "checked" is true, then I want to check the "checked" flag for each child. If for any children, if the "checked" flag is true, then I want to have data from parent name to children name separated by "/" in an array. For the above data, my final array will have.
[
"myName3/myName2/myName",
"myName3/myName2/myNamePart2",
"myName9/myName8/myName7",
"myName12/myName11/myName10"
]
My code is as follows which gives me correct output
const compute = data =>
data
.flatMap(x => x.checked || x.children)
.concat(data)
.filter(x => x && x.checked && x.data)
.map(x => createPath(x))
.map(x => x.join("/"));
const createPath = (node, currentPath = []) =>
node.parent
? createPath(node.parent, [node.data.name, ...currentPath])
: [node.data.name, ...currentPath];
In this solution, I just want to add one more condition which is as follows:
If the "checked" flag is true from child to parent, then in my result array I want to have the only parent.
For example, in my data above I have a node with children and parent as follows
"checked": true,
"children": [{
"checked": true,
"data": {
"name": "myName"
},
"parent": {
"checked":true,
"data": {
"name": "myName2"
},
"parent": {
"checked":true,
"data": {
"name": "myName3"
}
}
}
}
In this, all the "checked" flags from parent-child to parent or we can say from parent to child are true. Then in this case I want to have my output for this in the result array as follows:
[
"myName3",
"myName3/myName2/myNamePart2",
"myName9/myName8/myName7",
"myName12/myName11/myName10"
]
Only having "myName3" as all checked flag is true from child to parent. In case if "checked" true is not for child or parent then my code should work as it is working now. How I can do this?
let obj = {
"options": [
{
"id": "a",
"name": "a",
"options": [
{
"id": "a.1",
"options": [
{
"id": "a.1.1",
"name": "a.1.1"
},
{
"id": "a.1.2",
"name": "a.1.2"
},
{
"id": "a.1.3",
"name": "a.1.3"
}
]
},
{
"id": "a.2",
"name": "a.2",
"options": [
{
"id": "a.2.1",
"name": "a.2.1"
},
{
"id": "a.2.2",
"name": "a.2.2"
},
{
"id": "a.2.3",
"name": "a.2.3"
}
]
},
{
"id": "a.3",
"name": "a.3",
"options": [
{
"id": "a.3.1",
"name": "a.3.1"
},
{
"id": "a.3.2",
"name": "a.3.1"
},
{
"id": "a.3.3",
"name": "a.3.1"
}
]
}
]
},
{
"name": "b",
"id": "b",
"options": [
{
"id": "b.1",
"name": "b.1",
"options": [
{
"id": "b.1.1",
"name": "b.1.1"
},
{
"id": "b.1.2",
"name": "b.1.2"
},
{
"id": "b.1.3",
"name": "b.1.3"
}
]
},
{
"id": "b.2",
"name": "b.2",
"options": [
{
"id": "b.2.1",
"name": "b.2.1"
},
{
"id": "b.2.2",
"name": "b.2.2"
},
{
"id": "b.2.3",
"name": "b.2.3"
}
]
},
{
"id": "b.3",
"name": "b.3.1",
"options": [
{
"id": "b.3.1",
"name": "b.3.1"
},
{
"id": "b.3.2",
"name": "b.3.2"
},
{
"id": "b.3.3",
"name": "b.3.3"
}
]
}
]
},
{
"id": "c",
"name": "c",
"options": [
{
"id": "c.1",
"name": "c.1",
"options": [
{
"id": "c.1.1",
"name": "c.1.1"
},
{
"id": "c.1.2",
"name": "c.1.2"
},
{
"id": "c.1.3",
"name": "c.1.3"
}
]
},
{
"id": "c.2",
"name": "c.2",
"options": [
{
"id": "c.2.1",
"name": "c.2.1"
},
{
"id": "c.2.2",
"name": "c.2.2"
},
{
"id": "c.2.3",
"name": "c.2.3"
}
]
},
{
"id": "c.3",
"name": "c.3",
"options": [
{
"id": "c.3.1",
"name": "c.3.1"
},
{
"id": "c.3.2",
"name": "c.3.2"
},
{
"id": "c.3.3",
"name": "c.3.3"
}
]
}
]
}
]
}
" I have this object
I neeed to create a function getHierarchy, that an option ID as its input, finds the option in the
list and returns the ID of all it's parents.
for example,getHierarchy("a.1.3") should return the following result ["a","a.1","a.1.3"]
getHierarchy("c.3.3") should return the following result ["c","c.3","c.3.3"] "
assuming id is unique;
function getHierarchy(object, id, prev) {
if (!object.options) return;
for (const child of object.options) {
if(child.id === id) {
return [...prev, object.id, child.id];
}
result = getHierarchy(child, id, [...prev, object.id]);
if (result) return result;
}
}
getHierarchy(obj, "a.1.3", []);
Here an recursive solution i hope this helps you.
let obj = {
"options": [
{
"id": "a",
"name": "a",
"options": [
{
"id": "a.1",
"options": [
{
"id": "a.1.1",
"name": "a.1.1"
},
{
"id": "a.1.2",
"name": "a.1.2"
},
{
"id": "a.1.3",
"name": "a.1.3"
}
]
},
{
"id": "a.2",
"name": "a.2",
"options": [
{
"id": "a.2.1",
"name": "a.2.1"
},
{
"id": "a.2.2",
"name": "a.2.2"
},
{
"id": "a.2.3",
"name": "a.2.3"
}
]
},
{
"id": "a.3",
"name": "a.3",
"options": [
{
"id": "a.3.1",
"name": "a.3.1"
},
{
"id": "a.3.2",
"name": "a.3.1"
},
{
"id": "a.3.3",
"name": "a.3.1"
}
]
}
]
},
{
"name": "b",
"id": "b",
"options": [
{
"id": "b.1",
"name": "b.1",
"options": [
{
"id": "b.1.1",
"name": "b.1.1"
},
{
"id": "b.1.2",
"name": "b.1.2"
},
{
"id": "b.1.3",
"name": "b.1.3"
}
]
},
{
"id": "b.2",
"name": "b.2",
"options": [
{
"id": "b.2.1",
"name": "b.2.1"
},
{
"id": "b.2.2",
"name": "b.2.2"
},
{
"id": "b.2.3",
"name": "b.2.3"
}
]
},
{
"id": "b.3",
"name": "b.3.1",
"options": [
{
"id": "b.3.1",
"name": "b.3.1"
},
{
"id": "b.3.2",
"name": "b.3.2"
},
{
"id": "b.3.3",
"name": "b.3.3"
}
]
}
]
},
{
"id": "c",
"name": "c",
"options": [
{
"id": "c.1",
"name": "c.1",
"options": [
{
"id": "c.1.1",
"name": "c.1.1"
},
{
"id": "c.1.2",
"name": "c.1.2"
},
{
"id": "c.1.3",
"name": "c.1.3"
}
]
},
{
"id": "c.2",
"name": "c.2",
"options": [
{
"id": "c.2.1",
"name": "c.2.1"
},
{
"id": "c.2.2",
"name": "c.2.2"
},
{
"id": "c.2.3",
"name": "c.2.3"
}
]
},
{
"id": "c.3",
"name": "c.3",
"options": [
{
"id": "c.3.1",
"name": "c.3.1"
},
{
"id": "c.3.2",
"name": "c.3.2"
},
{
"id": "c.3.3",
"name": "c.3.3"
}
]
}
]
}
]
}
function getHierarchy(opts, path, index = 1, result = []) {
if(!opts) return result;
let objPaths = path.split(".");
let _path = objPaths.slice(0, index).join(".");
let option = opts.find(({id}) => id === _path);
if(!option) return result;
let { options, id } = option;
return getHierarchy(options, path, ++index, [...result,id]);
}
let result = getHierarchy(obj.options, "a.1.3");
console.log(result);
In vanilla JavaScript, how would I find unique locations from this object and make them keys, and place all items with that location as values. (can install lodash if necessary).
So this:
[
{
"item": {
"id": "cat"
},
"location": {
"id": "porch"
}
},
{
"item": {
"id": "dog"
},
"location": {
"id": "porch"
}
},
{
"item": {
"id": "snake"
},
"location": {
"id": "forest"
}
},
{
"item": {
"id": "bird"
},
"location": {
"id": "forest"
}
},
{
"item": {
"id": "beer"
},
"location": {
"id": "fridge"
}
}
]
Becomes this:
[
{
"porch": [
{
"id": "cat"
},
{
"id": "dog"
}
]
},
{
"forest": [
{
"id": "snake"
},
{
"id": "bird"
}
]
},
{
"fridge": [
{
"id": "beer"
}
]
}
]
PEN
// modified desired result
[
{
"location": {
"name": "porch",
"items": [
{
"title": "cat"
},
{
"title": "dog"
}
]
}
},
{
"location": {
"name": "forest",
"items": [
{
"title": "snake"
},
{
"title": "bird"
}
]
}
},
{
"location": {
"name": "fridge",
"items": [
{
"title": "beer"
}
]
}
}
]
let obj = [
{
"item": {
"id": "cat"
},
"location": {
"id": "porch"
}
},
{
"item": {
"id": "dog"
},
"location": {
"id": "porch"
}
},
{
"item": {
"id": "snake"
},
"location": {
"id": "forest"
}
},
{
"item": {
"id": "bird"
},
"location": {
"id": "forest"
}
},
{
"item": {
"id": "beer"
},
"location": {
"id": "fridge"
}
}
]
let result = {};
obj.forEach(({item, location}) => {
if(!result[location.id]) result[location.id] = []
result[location.id].push({title: item.id})
})
result = Object.keys(result).map(key => ({
"location": {
"name": key,
"items": result[key]
}
}))
result contains required output.
I have a raw data like this:
var data = {
"issues": [
{
"fields": {
"project": {
"key": "ProjectA"
},
"components": [
{
"name": "Component A"
},
{
"name": "Component C"
}
],
"priority": {
"name": "P0"
},
"status": {
"name": "Closed"
}
}
},
{
"fields": {
"project": {
"key": "ProjectA"
},
"components": [
{
"name": "Component B"
}
],
"priority": {
"name": "P1"
},
"status": {
"name": "Reopened"
}
}
},
{
"fields": {
"project": {
"key": "ProjectA"
},
"components": [
{
"name": "Component B"
}
],
"priority": {
"name": "P1"
},
"status": {
"name": "Closed"
}
}
},
{
"fields": {
"project": {
"key": "Project B"
},
"components": [
{
"name": "Component X"
}
],
"priority": {
"name": "P1"
},
"status": {
"name": "Closed"
}
}
}
]
};
If I give ProjectA as input, I would like to filter, group and aggregate the output as follows:
"components": [
{
"name": "Component A",
"priorities": [
{
"name": "P0",
"status": [
{
"name": "Open",
"count": 0
},
{
"name": "Reopened",
"count": 0
},
{
"name": "Closed",
"count": 1
}
]
},
{
"name": "P1",
"status": [
{
"name": "Open",
"count": 0
},
{
"name": "Reopened",
"count": 0
},
{
"name": "Closed",
"count": 0
}
]
}
]
},
{
"name": "Component B",
"priorities": [
{
"name": "P0",
"status": [
{
"name": "Open",
"count": 0
},
{
"name": "Reopened",
"count": 0
},
{
"name": "Closed",
"count": 0
}
]
},
{
"name": "P1",
"status": [
{
"name": "Open",
"count": 0
},
{
"name": "Reopened",
"count": 1
},
{
"name": "Closed",
"count": 1
}
]
}
]
},
{
"name": "Component C",
"priorities": [
{
"name": "P0",
"status": [
{
"name": "Open",
"count": 0
},
{
"name": "Reopened",
"count": 0
},
{
"name": "Closed",
"count": 1
}
]
},
{
"name": "P1",
"status": [
{
"name": "Open",
"count": 0
},
{
"name": "Reopened",
"count": 0
},
{
"name": "Closed",
"count": 0
}
]
}
]
}
]
I tried chaining, filtering, grouping, but not sure how to use this, since my data is too nested.
This is a proposal in plain javascript with first collecting all components and priorities and build then a result object and later increment the count of the status.
var data = { "issues": [{ "fields": { "project": { "key": "ProjectA" }, "components": [{ "name": "Component A" }, { "name": "Component C" }], "priority": { "name": "P0" }, "status": { "name": "Closed" } } }, { "fields": { "project": { "key": "ProjectA" }, "components": [{ "name": "Component B" }], "priority": { "name": "P1" }, "status": { "name": "Reopened" } } }, { "fields": { "project": { "key": "ProjectA" }, "components": [{ "name": "Component B" }], "priority": { "name": "P1" }, "status": { "name": "Closed" } } }, { "fields": { "project": { "key": "Project B" }, "components": [{ "name": "Component X" }], "priority": { "name": "P1" }, "status": { "name": "Closed" } } }] },
componentsO = Object.create(null),
prioritiesO = Object.create(null),
components,
priorities,
status_ = ['Open', 'Reopened', 'Closed'], // status collides in chrome with window.status
hash = Object.create(null),
getKey = function (a) { return a.join('|'); },
result = {};
data.issues.forEach(function (issue) {
if (issue.fields.project.key === 'ProjectA') {
issue.fields.components.forEach(function (component) {
componentsO[component.name] = true;
});
prioritiesO[issue.fields.priority.name] = true;
}
});
components = Object.keys(componentsO).sort();
priorities = Object.keys(prioritiesO).sort();
result.components = components.map(function (component) {
return {
name: component,
priorities: priorities.map(function (priority) {
return {
name: priority,
status: status_.map(function (status) {
var key = getKey([component, priority, status]);
hash[key] = { name: status, count: 0 };
return hash[key];
})
};
})
}
});
data.issues.forEach(function (issue) {
if (issue.fields.project.key === 'ProjectA') {
issue.fields.components.forEach(function (component) {
var key = getKey([component.name, issue.fields.priority.name, issue.fields.status.name]);
hash[key].count++;
});
}
});
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }