I have an array of objects which has further levels of children which follow the same pattern as that of their parents. A hierarchical tree view. My requirement is to do a recursion but in addition to that do updation to the parent array.
Input Array:
[
{
category_id: "1",
name: "Books",
parent_category: null,
children: [
{
category_id: "48",
name: "Arts, Film & Photography",
parent_category: "1",
children: [
{
category_id: "69",
name: "Architecture",
parent_category: "48",
children: [],
},
],
},
{
category_id: "20",
name: "Action & Adventure",
parent_category: "1",
children: [],
},
],
},
{
category_id: "2",
name: "Action & Adventure",
parent_category: null,
children: [],
},
]
I need to find and update all nodes that do not have further children with another key 'disabled: false' and those objects which has subsequent children with key 'disabled: true'.
expected result:
[
{
category_id: "1",
name: "Books",
parent_category: null,
disabled: true
children: [
{
category_id: "48",
name: "Arts, Film & Photography",
parent_category: "1",
disabled: true,
children: [
{
category_id: "69",
name: "Architecture",
parent_category: "48",
children: [],
disabled: false,
},
],
},
{
category_id: "20",
name: "Action & Adventure",
parent_category: "1",
children: [],
disabled: false,
},
],
},
{
category_id: "2",
name: "Action & Adventure",
parent_category: null,
children: [],
disabled: false,
},
]
Any help is appreciated and thanks in advance.
Below is the code that I tried, The problem is I cant align with the depth of the array with my updated array.
let newArray = []
const recur = (array) => {
array.map(childObj => {
/* console.log(childObj.category_id) */;
if(childObj.children.length) {
/* console.log('innerchild',childObj.category_id) */;
recur(childObj.children)
} else {
childObj['disabled'] = false
}
newArray.push(childObj);
})
}
recur(arr);
You could iterate the elements from the array and add a property, depending on the children length and iterate the children as well.
const
update = array => array.forEach(o => {
o.disabled = !!o.children.length;
update(o.children);
}),
data = [{ category_id: "1", name: "Books", parent_category: null, children: [{ category_id: "48", name: "Arts, Film & Photography", parent_category: "1", children: [{ category_id: "69", name: "Architecture", parent_category: "48", children: [] }] }, { category_id: "20", name: "Action & Adventure", parent_category: "1", children: [] }] }, { category_id: "2", name: "Action & Adventure", parent_category: null, children: [] }];
update(data);
console.log(data);
.as-console-wrapper { max-height: 100% !important; top: 0; }
If you do not like to mutate the data, you could map new objects.
const
update = ({ children, ...o }) => ({
...o,
disabled: !!children.length,
children: children.map(update)
}),
data = [{ category_id: "1", name: "Books", parent_category: null, children: [{ category_id: "48", name: "Arts, Film & Photography", parent_category: "1", children: [{ category_id: "69", name: "Architecture", parent_category: "48", children: [] }] }, { category_id: "20", name: "Action & Adventure", parent_category: "1", children: [] }] }, { category_id: "2", name: "Action & Adventure", parent_category: null, children: [] }];
console.log(data.map(update));
.as-console-wrapper { max-height: 100% !important; top: 0; }
Related
I would like to get the summed value of all the arrays in the "products" object (price * quantity). The summed value should be returned in the return.
Do you have any ideas how to do that?
{
"event": "checkout",
"ecommerce": {
"checkout": {
"actionField": {
"step": 2,
"option": "Initiate checkout",
"action": "checkout"
},
"products": [
{
"id": "52",
"name": "Turystyczna kuchenka gazowa SMILE-KN-03/1K",
"price": 161.788618,
"brand": "",
"category": "kuchenki-elektryczne-i-gazowe",
"variant": "",
"quantity": "1"
},
{
"id": "36",
"name": "Kuchnia gazowa MPM-51-KGF-21",
"price": 641.463415,
"brand": "",
"category": "kuchnie-gazowe",
"variant": "",
"quantity": "1"
}
]
}
},
"gtm.uniqueEventId": 12
}
const g = {
event: 'checkout',
ecommerce: {
checkout: {
actionField: {
step: 2,
option: 'Initiate checkout',
action: 'checkout',
},
products: [
{
id: '52',
name: 'Turystyczna kuchenka gazowa SMILE-KN-03/1K',
price: 161.788618,
brand: '',
category: 'kuchenki-elektryczne-i-gazowe',
variant: '',
quantity: '1',
},
{
id: '36',
name: 'Kuchnia gazowa MPM-51-KGF-21',
price: 641.463415,
brand: '',
category: 'kuchnie-gazowe',
variant: '',
quantity: '1',
},
],
},
},
'gtm.uniqueEventId': 12,
};
const c = g.ecommerce.checkout.products.reduce((acc, curr) => {
acc += curr.price * curr.quantity;
return acc;
}, 0);
console.log(c)
guess you want something like this?
i want to loop through array of objects and check for a particular property and add a new property "disabled" to the array.
below is the array of objects
const arr_obj = [
{
id: "1",
name: "name1",
type: "type2",
children: [
{
id: "2",
name: "name2",
type: "type4",
children: [
{
id: "3",
name: "name3",
type: "type5",
},
},
{
id: "4",
name: "name4",
type: "type3",
children: [
{
id: "5",
name: "name5",
type: "type4",
children: [],
},
],
},
id: "6",
name: "name6",
type: "type3",
children: [
{
id: "7",
name: "name7",
type: "type4",
children: [],
},
],
}
},
.....//similar objects
];
so from above array of objects i want to check for each object if type === type2 and if type2 then add property disabled: false if not disabled: true.
below is what i have tried
const new_obj = React.useMemo(() => {
return arr_obj.map((arr) => ({
...arr,
disabled: arr?.type !== "type2" ? true : false,
}));
}, [arr_obj]);
this adds disabled property only to outer object it doesnt add to children object.
output with above snippet is like below,
const new_arr = [
{
id: "1",
name: "name1",
type: "type2",
disabled: false,
children: [
{
id: "2",
name: "name2",
type: "type4",
children: [
{
id: "3",
name: "name3",
type: "type5",
},
},
{
id: "4",
name: "name4",
type: "type3",
children: [
{
id: "5",
name: "name5",
type: "type4",
children: [],
},
],
},
id: "6",
name: "name6",
type: "type3",
children: [
{
id: "7",
name: "name7",
type: "type4",
children: [],
},
],
}
},
.....//similar objects
];
expected output is like below,
const new_arr = [
{
id: "1",
name: "name1",
type: "type2",
disabled: false,
children: [
{
id: "2",
name: "name2",
type: "type4",
disabled: true,
children: [
{
id: "3",
name: "name3",
type: "type5",
disabled: true,
},
},
{
id: "4",
name: "name4",
type: "type3",
disabled: true,
children: [
{
id: "5",
name: "name5",
type: "type4",
disabled: true,
children: [],
},
],
},
id: "6",
name: "name6",
type: "type3",
disabled: true
children: [
{
id: "7",
name: "name7",
type: "type4",
disabled: true,
children: [],
},
],
}
},
.....//similar objects
];
How can i fix the above snippet such that it adds disabled property to children too. could someone help me with this. thanks.
EDIT:
tried answer is like below,
function loop_children(children) {
if (!children || children.lengh <=0) {
return;
} else {
return children.map((child) => {
...child,
disabled: child?.type !== "type2" ? true : false,
children: loop_children(children)
})
};
}
}
return arr_obj.map((arr) => ({
...arr,
disabled: arr?.type !== "type2" ? true : false,
children: loop_children(arr.children) //seems like a problem here in adding children field again
}));
but this adds children array under children again.
This code doesnt work. it adds field disabled to children but also adds children within children.
could someone help me with this. thanks.
Not sure why all the others are mapping, just alter the object with a simple recursive call when it has a children property.
const arr_obj = [{
id: "1",
name: "name1",
type: "type2",
children: [{
id: "2",
name: "name2",
type: "type4",
children: [{
id: "3",
name: "name3",
type: "type5",
}, ]
}, ]
}];
const disableEnableObj = (arr, type) => {
arr.forEach(obj => {
obj.disabled = obj.type !== type;
obj.children && disableEnableObj(obj.children, type);
});
}
disableEnableObj(arr_obj, 'type2');
console.log(arr_obj);
You have to loop through the children too. It should look something like this:
function loop_children(children) {
return children.map((child) => {
...child,
disabled: child?.type !== "type2" ? true : false,
children: loop_children(children)
})
}
return arr_obj.map((arr) => ({
...arr,
disabled: arr?.type !== "type2" ? true : false,
children: loop_children(children)
}));
I have two array like this:
let arr1 = [
{id: "1", icon: Images.icon.food, checkable: false, text: 'Dine-in', description: "coupon for dine in"},
{id: "2", icon: Images.icon.delivery, checkable: false, text: 'Delivery', description: "coupon for delivery"},
{id: "3", icon: Images.icon.collection, checkable: false, text: 'Collection', description: "coupon for collection"},
]
let arr2 = [
{description: "Dine in Service", id: "13", isActive: false, name: "Dine In", serviceDetail: null, serviceId: "1", slug: "dine-in"},
{description: "Delivery Service", id: "14", isActive: true, name: "Delivery", serviceDetail: null, serviceId: "2", slug:"delivery"},
{description: "Collection Service", id: "15", isActive: true, name: "Collection", serviceDetail: null, serviceId: "3", slug: "collection"}
]
How to compare id of arr1 equa to serviceId of arr2, and then set value checkable in arr1 equa to isActice in arr2
This is result I want to be:
result = [
{id: "1", icon: Images.icon.food, checkable: false, text: 'Dine-in', description: "coupon for dine in"},
{id: "2", icon: Images.icon.delivery, checkable: true, text: 'Delivery', description: "coupon for delivery"},
{id: "3", icon: Images.icon.collection, checkable: true, text: 'Collection', description: "coupon for collection"},
]
Means result like arr1 but change checkable equa to isActive of arr2.
Thank you.
Use can simply use find and use an arrow function to define the comparision expression. And then you can simply do the attribution directly
let arr1 = [
{id: "1", icon: 'food', checkable: false, text: 'Dine-in', description: "coupon for dine in"},
{id: "2", icon: 'delivery', checkable: false, text: 'Delivery', description: "coupon for delivery"},
{id: "3", icon: 'collection', checkable: false, text: 'Collection', description: "coupon for collection"},
];
let arr2 = [
{description: "Dine in Service", id: "13", isActive: false, name: "Dine In", serviceDetail: null, serviceId: "1", slug: "dine-in"},
{description: "Delivery Service", id: "14", isActive: true, name: "Delivery", serviceDetail: null, serviceId: "2", slug:"delivery"},
{description: "Collection Service", id: "15", isActive: true, name: "Collection", serviceDetail: null, serviceId: "3", slug: "collection"}
];
for(let i = 0; i < arr1.length; i++){
arr1[i].checkable = arr2.find(x => x.serviceId == arr1[i].id).isActive;
console.log(arr1[i]);
}
If you prefer a one line forEach
arr1.forEach((array1) => array1.checkable = arr2.find(x => x.serviceId == array1.id).isActive );
You could collect all active states in an object and map new object with the state.
let arr1 = [{ id: "1", icon: 'Images.icon.food', checkable: false, text: 'Dine-in', description: "coupon for dine in" }, { id: "2", icon: 'Images.icon.delivery', checkable: false, text: 'Delivery', description: "coupon for delivery" }, { id: "3", icon: 'Images.icon.collection', checkable: false, text: 'Collection', description: "coupon for collection" }],
arr2 = [{ description: "Dine in Service", id: "13", isActive: false, name: "Dine In", serviceDetail: null, serviceId: "1", slug: "dine-in" }, { description: "Delivery Service", id: "14", isActive: true, name: "Delivery", serviceDetail: null, serviceId: "2", slug:"delivery" }, { description: "Collection Service", id: "15", isActive: true, name: "Collection", serviceDetail: null, serviceId: "3", slug: "collection" }],
isActive = Object.fromEntries(arr2.map(({ serviceId, isActive }) => [serviceId, isActive])),
result = arr1.map(o => ({ ...o, isActive: isActive[o.id] }));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
I have an object that looks like this:
{
data: [
{ id: "1", state: "accepted", estimate_date: "2019-12-17" },
{ id: "2", state: "rejected", estimate_date: "2019-12-17" },
{ id: "3", state: "open", estimate_date: "2019-12-17" },
{ id: "4", state: "open", estimate_date: "2019-12-18" },
{ id: "5", state: "rejected", estimate_date: "2019-12-18" },
{ id: "6", state: "accepted", estimate_date: "2019-12-18" },
]
}
When I use lodash groupBy on the object like this:
const key = 'data';
const groupedEstimates = groupBy(estimateData[key], 'estimate_date');
It returns:
[
[
"2019-12-17"
],
[
[ { id: "1", state: "accepted" } ],
[ { id: "2", state: "rejected" } ],
[ { id: "3", state: "open" } ]
]
],
[
[
"2019-12-18"
],
[
[ { id: "4", state: "open" } ],
[ { id: "5", state: "rejected" } ],
[ { id: "6", state: "accepted" } ]
]
]
But now I'm trying to achieve something like this:
[
{
date: "2019-12-17",
items: [
{ id: "1", state: "accepted" },
{ id: "2", state: "rejected" },
{ id: "3", state: "open" },
]
},
{
date: "2019-12-18",
items: [
{ id: "4", state: "open" },
{ id: "5", state: "rejected" },
{ id: "6", state: "accepted" },
]
}
]
Except I don't know how to achieve that using lodash. It doesn't have to use lodash but I only used that at the start as it seemed an easy solution to my problem. Now that I'm trying to achieve a more sensible data structure I would like some insights on how to achieve it.
After grouping by the estimate_date property, iterate the groups object with _.map(). Generate the group's object by taking the key (2nd param) for the date property, and mapping the items to omit the estimate_date:
const estimateData = {"data":[{"id":"1","state":"accepted","estimate_date":"2019-12-17"},{"id":"2","state":"rejected","estimate_date":"2019-12-17"},{"id":"3","state":"open","estimate_date":"2019-12-17"},{"id":"4","state":"open","estimate_date":"2019-12-18"},{"id":"5","state":"rejected","estimate_date":"2019-12-18"},{"id":"6","state":"accepted","estimate_date":"2019-12-18"}]}
const groupedEstimates = _.map(
_.groupBy(estimateData.data, 'estimate_date'),
(items, date) => ({
date,
items: items.map(o => _.omit(o, 'estimate_date'))
})
)
console.log(groupedEstimates)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.js"></script>
I have a complex JavaScript object given below.
An example object:
var object= {
"name": "tfifkhul",
"id": "262761",
"children": [
{
"name": "rthrth",
"id": 0,
"children": [
{
"name": "test",
"id": "262762",
"children": []
}
]
},
{
"name": "rthsrth",
"id": 0,
"children": [
{
"name": "test",
"id": "262762",
"children": []
}
]
},
{
"name": "rthrthhrth",
"id": 0,
"children": [
{
"name": "test",
"id": "262762",
"children": [
{
"name": "rtjrtj",
"id": 0,
"children": [
{
"name": "fwefwefwef",
"id": "262768",
"children": []
}
]
},
{
"name": "hsrtjrtdjrtj",
"id": 0,
"children": [
{
"name": "we4yhesrhy",
"id": "262764",
"children": []
}
]
},
{
"name": "lol",
"id": "262763",
"children": [
{
"name": "fwefwefwef",
"id": "262768",
"children": [
{
"name": "87ok78",
"id": "262765",
"children": [
{
"name": "78o78",
"id": 0,
"children": [
{
"name": "we4yhesrhy",
"id": "262764",
"children": [
{
"name": "test1",
"id": 0,
"children": [
{
"name": "",
"id": "262766",
"children": []
}
]
},
{
"name": "test2",
"id": 0,
"children": [
{
"name": "",
"id": "262766",
"children": []
}
]
}
]
}
]
},
{
"name": "7o78o76o8",
"id": 0,
"children": [
{
"name": "",
"id": "262766",
"children": []
}
]
},
{
"name": "ko",
"id": 0,
"children": [
{
"name": "",
"id": "262767",
"children": []
}
]
}
]
}
]
}
]
}
]
}
]
}
]
};
I need to create a function to search for all matching values for key "id" with given value.
So far I have created one recursive function:
function searchOccurances(theObject, value,path) {
var result = null;
if(theObject instanceof Array) {
for(var i = 0; i < theObject.length; i++) {
result = searchOccurances(theObject[i],value,path+","+i);
}
}
else
{
for(prop in theObject) {
if(prop == 'id') {
if(theObject[prop] == value) {
keyOccurances.push(path);
}
}
if((theObject[prop] instanceof Array) || (theObject[prop] instanceof Object))
{
if((theObject[prop].length!=undefined)&&(theObject[prop].length!=0))
{
result = searchOccurances(theObject[prop],value,path+","+prop);
}
}
}
}
return result;
}
keyOccurances=[];
searchOccurances(object,262762,'');
console.log(keyOccurances);
//Output
[",children,0,children,0", ",children,1,children,0", ",children,2,children,0"] -- correct
keyOccurances=[];
searchOccurances(object,262768,'');
console.log(keyOccurances);
//Output
[",children,1,children,0,children,1,children,0", ",children,1,children,0,children,2,children,0"] --wrong
The function returns array of comma separated paths of matched value but doesn't seems to be getting right results. For the first call with value '262762' gives corrects path list but for value '262768' gives incorrect path list.
Kindly help.
I'd suggest to provide a better test object. Would you really have so many children with 'id = 0' in a real use case? Would you have 2 children with the same ID at all? That makes things pretty hard to debug.
Below is an example function that should work as expected.
function search(object, value) {
var res = [], searchPath;
(searchPath = function(children, path) {
var n, newPath;
for(n in children) {
if(typeof children[n].id !== 'undefined' && parseInt(children[n].id, 10) === value) {
res.push(path);
}
newPath = path.slice();
newPath.push(children[n].id);
searchPath(children[n].children, newPath);
}
})([ object ], []);
return res;
}
console.log(search(object, 262762));
console.log(search(object, 262768));
Output:
[["262761", 0], ["262761", 0], ["262761", 0]]
[["262761", 0, "262762", 0], ["262761", 0, "262762", "262763"]]
The above code is not (yet) bullet-proof but hopefully is it short enough to be easily understandable.
If I understand your questions correctly, you're looking for all paths where a specific id is present. I'd recommend not reinventing the wheel here and using an existing library. We use object-scan for most of our data processing now. It's powerful once you wrap your head around it. Here is how you'd answer your question
// const objectScan = require('object-scan');
const findKeys = (haystack, id) => objectScan(['**'], {
joined: true,
filterFn: ({ value }) => value.id === id
})(haystack);
const object = { name: 'tfifkhul', id: '262761', children: [{ name: 'rthrth', id: 0, children: [{ name: 'test', id: '262762', children: [] }] }, { name: 'rthsrth', id: 0, children: [{ name: 'test', id: '262762', children: [] }] }, { name: 'rthrthhrth', id: 0, children: [{ name: 'test', id: '262762', children: [{ name: 'rtjrtj', id: 0, children: [{ name: 'fwefwefwef', id: '262768', children: [] }] }, { name: 'hsrtjrtdjrtj', id: 0, children: [{ name: 'we4yhesrhy', id: '262764', children: [] }] }, { name: 'lol', id: '262763', children: [{ name: 'fwefwefwef', id: '262768', children: [{ name: '87ok78', id: '262765', children: [{ name: '78o78', id: 0, children: [{ name: 'we4yhesrhy', id: '262764', children: [{ name: 'test1', id: 0, children: [{ name: '', id: '262766', children: [] }] }, { name: 'test2', id: 0, children: [{ name: '', id: '262766', children: [] }] }] }] }, { name: '7o78o76o8', id: 0, children: [{ name: '', id: '262766', children: [] }] }, { name: 'ko', id: 0, children: [{ name: '', id: '262767', children: [] }] }] }] }] }] }] }] };
console.log(findKeys(object, '262762'));
/* =>
[ 'children[2].children[0]',
'children[1].children[0]',
'children[0].children[0]' ]
*/
console.log(findKeys(object, '262768'));
/* =>
[ 'children[2].children[0].children[2].children[0]',
'children[2].children[0].children[0].children[0]' ]
*/
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://bundle.run/object-scan#13.8.0"></script>
Disclaimer: I'm the author of object-scan
Edit: The accepted answer didn't make much sense to me (based on the question), but here is how you could generate the same output
// const objectScan = require('object-scan');
const findIdPath = (haystack, id) => objectScan(['**'], {
reverse: false,
filterFn: ({ value, parents, context }) => {
if (value.id === id) {
context.push(parents.filter((p) => 'id' in p).map((p) => p.id).reverse());
}
}
})(haystack, []);
const object = { name: 'tfifkhul', id: '262761', children: [{ name: 'rthrth', id: 0, children: [{ name: 'test', id: '262762', children: [] }] }, { name: 'rthsrth', id: 0, children: [{ name: 'test', id: '262762', children: [] }] }, { name: 'rthrthhrth', id: 0, children: [{ name: 'test', id: '262762', children: [{ name: 'rtjrtj', id: 0, children: [{ name: 'fwefwefwef', id: '262768', children: [] }] }, { name: 'hsrtjrtdjrtj', id: 0, children: [{ name: 'we4yhesrhy', id: '262764', children: [] }] }, { name: 'lol', id: '262763', children: [{ name: 'fwefwefwef', id: '262768', children: [{ name: '87ok78', id: '262765', children: [{ name: '78o78', id: 0, children: [{ name: 'we4yhesrhy', id: '262764', children: [{ name: 'test1', id: 0, children: [{ name: '', id: '262766', children: [] }] }, { name: 'test2', id: 0, children: [{ name: '', id: '262766', children: [] }] }] }] }, { name: '7o78o76o8', id: 0, children: [{ name: '', id: '262766', children: [] }] }, { name: 'ko', id: 0, children: [{ name: '', id: '262767', children: [] }] }] }] }] }] }] }] };
console.log(findIdPath(object, '262762'));
// => [ [ '262761', 0 ], [ '262761', 0 ], [ '262761', 0 ] ]
console.log(findIdPath(object, '262768'));
// => [ [ '262761', 0, '262762', 0 ], [ '262761', 0, '262762', '262763' ] ]
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://bundle.run/object-scan#13.8.0"></script>
Disclaimer: I'm the author of object-scan