I'm having hard time figuring out how to do this recursive map function.
I have an array that look like this.
var array = [
{
id: 1,
label: 'Satisfied customers',
children: [
{
id: 2,
label: 'Good food',
icon: 'restaurant_menu',
children: [
{ id: 3, label: 'Quality ingredients'},
{ id: 4, label: 'Good recipe' }
]
},
{
id: 5,
label: 'Good service',
icon: 'room_service',
children: [
{ id: 6, label: 'Prompt attention' },
{ id: 7, label: 'Professional waiter' }
]
},
{
id: 8,
label: 'Pleasant surroundings',
icon: 'photo',
children: [
{
id: 9,
label: 'Happy atmosphere (not tickable)',
tickable: false,
},
{
id: 10,
label: 'Good table presentation (disabled node)',
disabled: true,
},
{
id: 11,
label: 'Pleasing decor',
}
]
},
{
id: 12,
label: 'Extra information (has no tick)',
noTick: true,
icon: 'photo'
},
{
id: 13,
label: 'Forced tick strategy (to "strict" in this case)',
tickStrategy: 'strict',
icon: 'school',
children: [
{
id: 14,
label: 'Happy atmosphere',
},
{
id: 15,
label: 'Good table presentation',
},
{
id: 16,
label: 'Very pleasing decor',
}
]
}
]
}
];
This is the array looks like...
As you can see the children is recursive.
I need to put them into one array.
My code doesn't work an has an error.
const result = [];
const map = (e) => {
result.push({
id: e.id,
label: e.label,
})
e.children.map(map)
};
array.map(map);
the error is on e.children.map(map).
I need to push them all in array variable but I don't know how to do this. TY
You need to check if current item has children element, and you can use forEach instead because map return new array and forEach just go throw each element.
const cb = (e) => {
res.push({
id: e.id,
label: e.label,
});
e.children && e.children.forEach(cb);
}
array.forEach(cb);
A great way to learn about mutual recursion using vanilla JavaScript -
const transform1 = ({ id = 0, label = "", children = [] }) =>
[ { id, label }, ...transformAll (children) ] // calls transformAll
const transformAll = (children = []) =>
children .flatMap (c => transform1 (c)) // calls transform1
console.log(transformAll(array))
Output -
[
{
"id": 1,
"label": "Satisfied customers"
},
{
"id": 2,
"label": "Good food"
},
{
"id": 3,
"label": "Quality ingredients"
},
{
"id": 4,
"label": "Good recipe"
},
{
"id": 5,
"label": "Good service"
},
{
"id": 6,
"label": "Prompt attention"
},
{
"id": 7,
"label": "Professional waiter"
},
{
"id": 8,
"label": "Pleasant surroundings"
},
{
"id": 9,
"label": "Happy atmosphere (not tickable)"
},
{
"id": 10,
"label": "Good table presentation (disabled node)"
},
{
"id": 11,
"label": "Pleasing decor"
},
{
"id": 12,
"label": "Extra information (has no tick)"
},
{
"id": 13,
"label": "Forced tick strategy (to \"strict\" in this case)"
},
{
"id": 14,
"label": "Happy atmosphere"
},
{
"id": 15,
"label": "Good table presentation"
},
{
"id": 16,
"label": "Very pleasing decor"
}
]
Expand the snippet below the verify the results in your own browser -
var array = [
{
id: 1,
label: 'Satisfied customers',
children: [
{
id: 2,
label: 'Good food',
icon: 'restaurant_menu',
children: [
{ id: 3, label: 'Quality ingredients'},
{ id: 4, label: 'Good recipe' }
]
},
{
id: 5,
label: 'Good service',
icon: 'room_service',
children: [
{ id: 6, label: 'Prompt attention' },
{ id: 7, label: 'Professional waiter' }
]
},
{
id: 8,
label: 'Pleasant surroundings',
icon: 'photo',
children: [
{
id: 9,
label: 'Happy atmosphere (not tickable)',
tickable: false,
},
{
id: 10,
label: 'Good table presentation (disabled node)',
disabled: true,
},
{
id: 11,
label: 'Pleasing decor',
}
]
},
{
id: 12,
label: 'Extra information (has no tick)',
noTick: true,
icon: 'photo'
},
{
id: 13,
label: 'Forced tick strategy (to "strict" in this case)',
tickStrategy: 'strict',
icon: 'school',
children: [
{
id: 14,
label: 'Happy atmosphere',
},
{
id: 15,
label: 'Good table presentation',
},
{
id: 16,
label: 'Very pleasing decor',
}
]
}
]
}
];
const transform1 = ({ id = 0, label = "", children = [] }) =>
[ { id, label }, ... transformAll (children) ]
const transformAll = (children = []) =>
children .flatMap (c => transform1 (c))
console.log(transformAll(array))
A tour of Flat Earth
If you've never seen .flatMap before -
xs.flatMap(f) == xs.map(f).reduce((a,b) => a.concat(b), [])
xs.flatMap(f) == xs.reduce((a,b) => a.concat(f(b)), [])
It's best seen with a simple demo -
const twice = x =>
[ x, x ]
console .log
( [ 'a', 'b', 'c' ] .flatMap (twice) // [ 'a', 'a', 'b', 'b', 'c', 'c' ]
, [ 1, 2, 3, 4, 5 ] .flatMap (twice) // [ 1, 1, 2, 2, 3, 3, 4, 4, 5, 5 ]
)
flatMap is useful for all kinds of things -
const tree =
[ 0, [ 1 ], [ 2, [ 3 ], [ 4, [ 5 ] ] ] ]
const all = ([ value, ...children ]) =>
[ value, ...children .flatMap (all) ]
console .log (all (tree))
// [ 0, 1, 2, 3, 4, 5 ]
really cool things -
const ranks =
[ 'J', 'Q', 'K', 'A' ]
const suits =
[ '♡', '♢', '♤', '♧' ]
const cards =
ranks .flatMap (r =>
suits .flatMap (s =>
[ [ r, s ] ]
))
console .log (cards)
// [ ['J','♡'], ['J','♢'], ['J','♤'], ['J','♧']
// , ['Q','♡'], ['Q','♢'], ['Q','♤'], ['Q','♧']
// , ['K','♡'], ['K','♢'], ['K','♤'], ['K','♧']
// , ['A','♡'], ['A','♢'], ['A','♤'], ['A','♧']
// ]
Related
I have an array of objects that I have to process and then include data from it to HTML. The problem is that now everything is displayed one by one and if I try to toggle class to li element, it toggles to all li elements. The data can be changed dynamically so I cannot access elements by ID.
I want to access the "main" category first (desserts, water, tea), then be able to proceed to subcategory of the selected main category etc. Further I will create a menu like in the screenshot (a one menu, a pic shows different states of it)
I have 2 problems now:
For some reason the main category isn't showing at all - why is it happening?
How can I access the elements according to hierarchy and nesting?
const menu = [
{
id: 1,
name: "Desserts",
groups: [
{
id: 2,
name: "Cold",
groups: [
{
id: 3,
name: "Ice Cream",
groups: []
},
{
id: 4,
name: "Cold brew coffee",
groups: []
}
]
},
{
id: 5,
name: "Hot",
groups: [
{
id: 6,
name: "Pancakes",
groups: []
},
{
id: 7,
name: "Apple pie",
groups: []
}
]
}
]
},
{
id: 8,
name: "Water",
groups: []
},
{
id: 7,
name: "Tea",
groups: [
{
id: 8,
name: "Green tea",
groups: [
{
id: 9,
name: "With Jasmine",
groups: []
},
{
id: 10,
name: "Plain",
groups: []
}
]
},
{
id: 11,
name: "Black Tea",
groups: []
}
]
}
];
let menuEl = document.querySelector(".funding__categories");
addElements(menuEl, menu[0].groups);
function addElements(parent, arr) {
let allCategories = parent.appendChild(document.createElement("ul"));
allCategories.classList.add("parent");
arr.forEach((el) => {
let subCategory = allCategories.appendChild(document.createElement("li"));
subCategory.dataset.id = el.id;
subCategory.textContent = el.name;
if (el.groups.length > 0) addElements(subCategory, el.groups);
});
}
<div class="funding__categories"></div>
The first problem is because you're passing menu[0].groups to the function. That skips over the top-level categories and starts at the items nested under Desserts. Pass menu as the argument.
I don't understand the second question. Access them in what way?
const menu = [
{
id: 1,
name: "Desserts",
groups: [
{
id: 2,
name: "Cold",
groups: [
{
id: 3,
name: "Ice Cream",
groups: []
},
{
id: 4,
name: "Cold brew coffee",
groups: []
}
]
},
{
id: 5,
name: "Hot",
groups: [
{
id: 6,
name: "Pancakes",
groups: []
},
{
id: 7,
name: "Apple pie",
groups: []
}
]
}
]
},
{
id: 8,
name: "Water",
groups: []
},
{
id: 7,
name: "Tea",
groups: [
{
id: 8,
name: "Green tea",
groups: [
{
id: 9,
name: "With Jasmine",
groups: []
},
{
id: 10,
name: "Plain",
groups: []
}
]
},
{
id: 11,
name: "Black Tea",
groups: []
}
]
}
];
let menuEl = document.querySelector(".funding__categories");
addElements(menuEl, menu);
function addElements(parent, arr) {
let allCategories = parent.appendChild(document.createElement("ul"));
allCategories.classList.add("parent");
arr.forEach((el) => {
let subCategory = allCategories.appendChild(document.createElement("li"));
subCategory.dataset.id = el.id;
subCategory.textContent = el.name;
if (el.groups.length > 0) addElements(subCategory, el.groups);
});
}
<div class="funding__categories"></div>
Here is my code
https://codepen.io/hobbesa/pen/jOGREKy?editors=1111
I am trying to get price of only weekdays.How do I do that?
this.getPriceWeekDay = this.StrategypricingDetail.map((rec) =>
rec.map((daysOfWeek) => {
return daysOfWeek;
}),
You can first, filter items which doesn't contain Saturday or Sunday by using filter and find methods, and then iterate over filtered list by map method to extract just price property, like this:
let StrategypricingDetail = [ { id: 3, name: "test", price: 30, daysOfWeek: [ { id: 1, name: "Monday" }, { id: 2, name: "Tuesday" }, { id: 3, name: "Wednesday" } ] }, { id: 23, name: "Testing2", price: 10, daysOfWeek: [ { id: 1, name: "Monday" }, { id: 2, name: "Tuesday" } ] }, { id: 13, name: "Testing3", price: 14, daysOfWeek: [ { id: 1, name: "Saturaday" }, { id: 2, name: "Sunday" } ] } ];
const weekDaysPrice = StrategypricingDetail.filter(({daysOfWeek}) => !daysOfWeek.find(({name}) => name == 'Saturaday' || name == 'Sunday')).map(({price}) => price);
console.log(weekDaysPrice);
One solution you could use below.
let strategypricingDetail = [
{
id: 3,
name: "test",
price: 30,
daysOfWeek: [
{ id: 1, name: "Monday" },
{ id: 2, name: "Tuesday" },
{ id: 3, name: "Wednesday" }
]
},
{
id: 23,
name: "Testing2",
price: 10,
daysOfWeek: [
{ id: 1, name: "Monday" },
{ id: 2, name: "Tuesday" }
]
},
{
id: 13,
name: "Testing3",
price: 14,
daysOfWeek: [
{ id: 1, name: "Saturaday" },
{ id: 2, name: "Sunday" }
]
}
];
let finalList = [];
// Create list of eligible days for the search
let daysToInclude = [
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday"
];
strategypricingDetail.forEach((item, index) => {
for (let i = 0; i < item.daysOfWeek.length; i++) {
// Check if the day is in the eligible list
if (daysToInclude.includes(item.daysOfWeek[i].name)) {
finalList.push(item);
break;
// When found, break out of the search after adding it to the final list.
}
}
});
console.log(finalList)
In this solution, you loop through the available items, and then compare each item's 'daysOfWeel' list with a list of eligible days.
As soon as it finds one, it'll stop searching, and add that item to a new list, until you end with the list of appropriate days.
//StrategypricingDetail is the whole json
const StrategypricingDetail = [
{
id: 3,
name: "test",
price: 30,
daysOfWeek: [
{ id: 1, name: "Monday" },
{ id: 2, name: "Tuesday" },
{ id: 3, name: "Wednesday" }
]
},
{
id: 23,
name: "Testing2",
price: 10,
daysOfWeek: [
{ id: 1, name: "Monday" },
{ id: 2, name: "Tuesday" }
]
},
{
id: 13,
name: "Testing3",
price: 14,
daysOfWeek: [
{ id: 1, name: "Saturaday" },
{ id: 2, name: "Sunday" }
]
}
];
const weekends = ["Saturaday","Sunday"]
// Approach
// we have to filter some element based on condition
// condition will be days should not belong to weekends
this.getPriceWeekDay = StrategypricingDetail.filter((rec) => {
for(let dayIndex = 0 ; dayIndex < rec.daysOfWeek.length; dayIndex++){
const dayName = rec.daysOfWeek[dayIndex].name;
if(weekends.includes(dayName)){
return;
}
}
return rec;
}).map(({price}) => price);
console.log(getPriceWeekDay);
My nested json array looks like:
[
{
id: 1,
name: "Mike",
children: [
{ id: 2, name: "MikeC1" },
{ id: 3, name: "MikeC2" },
{
id: 4, name: "MikeC3",
children: [{ id: 5, name: "MikeCC1" }]
},
]
},
{
id: 6,
name: "Json",
children: [
{ id: 7, name: "JsonC1" },
{ id: 8, name: "JsonC2" },
{
id: 9, name: "JsonC3",
children: [{ id: 10, name: "JsonCC1" },{ id: 11, name: "JsonCC2" }]
},
]
}
]
Now I get a id like "11"
then get the parent ids array in json like [6,9,11]
How to do?
var id = 11
console.log(findParent(id))
//result is [6,9,11]
You need to do recursive search
const persons = [
{
id: 1,
name: "Mike",
children: [
{ id: 2, name: "MikeC1" },
{ id: 3, name: "MikeC2" },
{
id: 4, name: "MikeC3",
children: [{ id: 5, name: "MikeCC1" }]
},
]
},
{
id: 6,
name: "Json",
children: [
{ id: 7, name: "JsonC1" },
{ id: 8, name: "JsonC2" },
{
id: 9, name: "JsonC3",
children: [{ id: 10, name: "JsonCC1" },{ id: 11, name: "JsonCC2" }]
},
]
}
];
function searchRecursive(items, id) {
const allIds = [];
items.forEach(item => {
if(item.id === id) {
allIds.push(item.id);
}
else if(item.children) {
const ids = searchRecursive(item.children, id);
if(ids.length) allIds.push(item.id);
ids.forEach(id => allIds.push(id));
}
});
return allIds;
}
console.log(searchRecursive(persons, 11));
in my current angular project I have to change a JSON array so that it can be displayed in a tree. So that the tree can output the object, a nested array must be packed in an object.
Expected form / output:
this.nodes = [
{
id: 1,
name: 'root1',
children: [
{ id: 2, name: 'child1' },
{ id: 3, name: 'child2' }
]
}, ....
My current form:
[{
id: 1,
name: 'root1',
name2: [{
id: 2,
name: 'child1'
},
{
id: 3,
name: 'child2',
name3: [{
id: 3,
name: 'child2'
}],
},
]
}], ...
How can I cange my 'name2' or 'name3' to children?
I've got this fat one liner:
const rename = (array) => array.map(item => Object.fromEntries(Object.entries(item).map(([k, v]) => k.match(/name\d+/) ? ['children', rename(v)] : [k, v])));
const nodes = [{
id: 1,
name: 'root1',
name2: [
{
id: 2,
name: 'child1'
},
{
id: 3,
name: 'child2',
name3: [
{
id: 3,
name: 'child2'
}
],
},
]
}];
const rename = (array) => array.map(item => Object.fromEntries(Object.entries(item).map(([k, v]) => k.match(/name\d+/) ? ['children', rename(v)] : [k, v])));
console.log(JSON.stringify(rename(nodes), null, 4));
// [
// {
// "id": 1,
// "name": "root1",
// "children": [
// {
// "id": 2,
// "name": "child1"
// },
// {
// "id": 3,
// "name": "child2",
// "children": [
// {
// "id": 3,
// "name": "child2"
// }
// ]
// }
// ]
// }
// ]
I have a data that needs to be recursive but I don't know how to implement it. Here is my data.
All I need to do is to look like this.
[
{
id: 1,
label: 'Satisfied customers',
children: [
{
id: 2,
label: 'Good food',
icon: 'restaurant_menu',
children: [
{ id: 3, label: 'Quality ingredients'},
{ id: 4, label: 'Good recipe' }
]
},
{
id: 5,
label: 'Good service',
icon: 'room_service',
children: [
{ id: 6, label: 'Prompt attention' },
{ id: 7, label: 'Professional waiter' }
]
},
{
id: 8,
label: 'Pleasant surroundings',
icon: 'photo',
children: [
{
id: 9,
label: 'Happy atmosphere (not tickable)',
tickable: false,
},
{
id: 10,
label: 'Good table presentation (disabled node)',
disabled: true,
},
{
id: 11,
label: 'Pleasing decor',
}
]
},
{
id: 12,
label: 'Extra information (has no tick)',
noTick: true,
icon: 'photo'
},
{
id: 13,
label: 'Forced tick strategy (to "strict" in this case)',
tickStrategy: 'strict',
icon: 'school',
children: [
{
id: 14,
label: 'Happy atmosphere',
},
{
id: 15,
label: 'Good table presentation',
},
{
id: 16,
label: 'Very pleasing decor',
}
]
}
]
}
]
My code doesn't works... it's just a map without any recursion.
categories.map(e => {
console.log(e.all_children)
return {
id: e.id,
label: e.name,
children: _.values(e).map(v => {
return { id: v.id, label: e.name }
})
}
})
I don't really know how to do it. If you have any idea on how to do it please help me. I've been searching how to do it using lodash but I can't find any relevant code. I'm not very good in javascript.
You need to specify a function for later mapping inside of the callback.
const
map = e => ({
id: e.id,
label: e.name,
children: e.all_children.map(map) // recursive call
}),
tree = categories.map(map);
To get all properties without all_children, you could take rest parameters ... for properties and separate just the children property for recursive mapping.
const
map = ({ all_children = [], ...o }) => ({
...o,
children: all_children.map(map) // recursive call
}),
tree = categories.map(map);