Related
I am trying to keep only children from each parent if it matches a child's id from a given list of parent/child ids.
var parentIds = ['1','2','3'];
var childrenIds = ['2','3','5'];
Parent with id '1' should only have children with id '2', parent with id '2' should only have children with id '3' and parent with id '3' should only have children with id '5'. So, parent id matches children id in same position from both arrays.
Example of initial object:
var object = [
{
name: "parent1",
id: 1,
option_values: [
{
name: "child1",
id: 1
},
{
name: "child2",
id: 2
},
{
name: "child8",
id: 8
}
]
},
{
name: "parent2",
id: 2,
option_values: [
{
name: "child3",
id: 3
},
{
name: "child1",
id: 1
},
{
name: "child9",
id: 9
}
]
},
{
name: "parent3",
id: 3,
option_values: [
{
name: "child5",
id: 5
},
{
name: "child4",
id: 4
},
{
name: "child13",
id: 13
}
]
},
]
Expected result:
var result = [
{
name: "parent1",
id: 1,
option_values: [
{
name: "child2",
id: 2
}
]
},
{
name: "parent2",
id: 2,
option_values: [
{
name: "child3",
id: 3
}
]
},
{
name: "parent3",
id: 3,
option_values: [
{
name: "child5",
id: 5
}
]
},
]
Loop through the objects. Find the index of the parent ID in the parentIds array, then get the corresponding element of childIds. Then filter the option_values array using that child ID.
var object = [{
name: "parent1",
id: 1,
option_values: [{
name: "child1",
id: 1
},
{
name: "child2",
id: 2
},
{
name: "child8",
id: 8
}
]
},
{
name: "parent2",
id: 2,
option_values: [{
name: "child3",
id: 3
},
{
name: "child1",
id: 1
},
{
name: "child9",
id: 9
}
]
},
{
name: "parent3",
id: 3,
option_values: [{
name: "child5",
id: 5
},
{
name: "child4",
id: 4
},
{
name: "child13",
id: 13
}
]
},
];
var parentIds = ['1', '2', '3'];
var childrenIds = ['2', '3', '5'];
object.forEach(obj => {
let idIndex = parentIds.indexOf(String(obj.id));
if (idIndex != -1) {
let childId = parseInt(childrenIds[idIndex]);
obj.option_values = obj.option_values.filter(child => child.id == childId);
}
});
console.log(object);
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>
I have an object like this;
var authList = {
"first": [{
Id: 1,
name: "test1"
}, {
Id: 2,
name: "test2"
}, {
Id: 3,
name: "test3"
}],
"second": [{
Id: 3,
name: "test3"
}],
"third": [{
Id: 2,
name: "test2"
}, {
Id: 3,
name: "test3"
}],
...may be n unit
};
how do i get users that are in each array?
example out;
response = [{ Id: 3, name: "test3" }];
In set theory terms you want to find the intersection between the sets of users. While JavaScript can transform arrays into sets, it does not have an intersection function but you can mix arrays and sets and use filters instead:
var authList = {
"first": [{
Id: 1,
name: "test1"
}, {
Id: 2,
name: "test2"
}, {
Id: 3,
name: "test3"
}],
"second": [{
Id: 3,
name: "test3"
}],
"third": [{
Id: 2,
name: "test2"
}, {
Id: 3,
name: "test3"
}],
};
let values = Object.values(authList);
let result = values[0].map(x => x.Id);
for(let i=1; i<values.length;i++) {result = result.filter(x => new Set(values[i].map(x => x.Id)).has(x))}
console.log(result);
This snippet creates an array of ids and stores them in the result variable, in this case the result = [3]. If you want the whole object instead you can use a map to map identifiers to objects again.
var authList = {
"first": [{
Id: 1,
name: "test1"
}, {
Id: 2,
name: "test2"
}, {
Id: 3,
name: "test3"
}],
"second": [{
Id: 3,
name: "test3"
}],
"third": [{
Id: 2,
name: "test2"
}, {
Id: 3,
name: "test3"
}],
};
let users = []
Object.keys(authList).map((res) => {
authList[res].map(res => {
users.push(res)
})
})
console.log('users', users)
this code works
var authList = {
"first": [{ Id: 1, name: "test1" }, { Id: 2, name: "test2" }, { Id: 3, name: "test3" }],
"second": [{ Id: 3, name: "test3" }],
"third": [{ Id: 2, name: "test2" }, { Id: 3, name: "test3"],
};
var keys = Object.keys(authList);
var resultArray = null;
for (var j = 0; j < keys.length; j++) {
if (!resultArray) {
resultArray = authList[keys[j]];
}
var sArray = authList[keys[j + 1]];
if (sArray) {
resultArray = resultArray.filter(a => sArray.some(b => a.Id === b.Id));
}
}
console.log(resultArray);
Here's a solution from pilchard's idea. Utilizing Array#reduce function to traverse the entire value set and find out the common items:
var authList = {
"first": [{
Id: 1,
name: "test1"
}, {
Id: 2,
name: "test2"
}, {
Id: 3,
name: "test3"
}],
"second": [{
Id: 3,
name: "test3"
}],
"third": [{
Id: 2,
name: "test2"
}, {
Id: 3,
name: "test3"
}]
};
const commonItems = Object.values(authList).reduce((acc, cur) =>
acc.filter(obj1 => cur.find(obj2 =>
Object.entries(obj2).every(([k, v]) => obj1[k] === v))
)
);
console.log(commonItems);
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));
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','♧']
// ]