manipulate nested array of objects to access children values - javascript

I have a nested array of objects:
const nestArray = [
{
title: "title 1",
children: [
{
title: "title 1-1",
children: [
{ title: "title 1-1-1", children: [...] },
{ title: "title 1-1-2", children: [...] }
]
},
{
title: "title 1-2",
children: [
{ title: "title 1-2-1", children: [...] },
{ title: "title 1-2-2", children: [...] }
]
},
]
},
{...},
{...}
]
All objects have the same interface:
interface Obj {
title: string
children: Obj[]
}
I need to put a new key into each object called keys.
The keys will keep all its children's titles alongside its own title. So the final result should be:
const nestArray = [
{
title: "title 1",
keys: ["title 1", "title 1-1", "title 1-1-1", "title 1-1-2"],
children: [
{
title: "title 1-1",
keys: ["title 1-1", "title 1-1-1", "title 1-1-2"],
children: [
{ title: "title 1-1-1", keys: ["title 1-1-1"], children: [] },
{ title: "title 1-1-2", keys: ["title 1-1-2"], children: [] }
]
},
{
title: "title 1-2",
keys: ["title 1-2", "title 1-2-1", "title 1-2-2"],
children: [
{ title: "title 1-2-1", keys: ["title 1-2-1"], children: [] },
{ title: "title 1-2-2", keys: ["title 1-2-2"], children: [] }
]
},
]
},
{...},
{...}
]
so the interface will be changed as:
interface Obj {
title: string
children: Obj[]
keys: string[]
}
I searched a lot but couldn't find any solution on the internet. I tried to solve this problem on my own, using recursive functions but still, I couldn't do it.
Using lodash is fine
What i've tried so far:
const mapTitlesToKeys = (obj) => {
obj.keys = [obj.title];
obj.children.forEach((childObj) => {
mapTitlesToKeys(childObj);
obj.keys.push(childObj.title);
});
};
nestArray.forEach((obj) => {
mapTitlesToKeys(obj);
});
console.log(nestArray);
results in:
[
{
title: "title 1",
keys: ['title 1', 'title 1-1', 'title 1-2'], // <-- should be ["title 1", "title 1-1", "title 1-1-1", "title 1-1-2"]
children: [
{
title: "title 1-1",
keys: ['title 1-1', 'title 1-1-1', 'title 1-1-2'], // <-- should be ["title 1-1", "title 1-1-1", "title 1-1-2"]
children: [
{
title: "title 1-1-1",
keys: ["title 1-1-1"], // <-- fine
children: []
},
{
title: "title 1-1-2",
keys: ["title 1-1-2"], // <-- fine
children: []
}
]
},
{
title: "title 1-2",
keys: ['title 1-2', 'title 1-2-1', 'title 1-2-2'], // <-- should be ["title 1-2", "title 1-2-1", "title 1-2-2"]
children: [
{
title: "title 1-2-1",
keys: ["title 1-2-1"], // <-- fine
children: []
},
{
title: "title 1-2-2",
keys: ["title 1-2-2"], // <-- fine
children: []
}
]
},
]
},
{...},
{...}
]

Presented below is one possible way to achieve the desired objective.
Code Snippet
const addKeysTo = arr => (
arr.map(
({ title, children }) => {
const cArr = addKeysTo(children);
keys = [title, ...cArr.flatMap(({ keys }) => keys )];
return ({ title, keys, children: cArr });
}
)
);
/* explanation of the method
// method to add "keys" array to each elt
const addKeysTo = arr => (
// use ".map()" to iterate over given array
arr.map(
// destructure to access "title", "children"
({ title, children }) => {
// recurse to obtain updated childre-narray
const cArr = addKeysTo(children);
// construct "keys" array for current elt
keys = [title, ...cArr.flatMap(({ keys }) => keys )];
// explicit return of current elt of "arr" array with "keys" prop
return ({ title, keys, children: cArr });
}
) // implicit return from the method
);
*/
const nestArray = [{
title: "title 1",
children: [{
title: "title 1-1",
children: [{
title: "title 1-1-1",
children: []
},
{
title: "title 1-1-2",
children: []
}
]
},
{
title: "title 1-2",
children: [{
title: "title 1-2-1",
children: []
},
{
title: "title 1-2-2",
children: []
}
]
},
]
}];
console.log(
'added keys to nested-objects array...\n',
addKeysTo(nestArray)
);
.as-console-wrapper { max-height: 100% !important; top: 0 }
Explanation
Inline comments added to the snippet above.

Something like this? (Shorter version... No need to keep the old one)
const nestArray = [{
title: "title 1",
children: [{
title: "title 1-1",
children: [{
title: "title 1-1-1",
children: []
},
{
title: "title 1-1-2",
children: []
}
]
},
{
title: "title 1-2",
children: [{
title: "title 1-2-1",
children: []
},
{
title: "title 1-2-2",
children: []
}
]
},
]
}]
function add_keys_obj(obj) {
obj.keys = [obj.title];
obj.children.forEach(function(child) {
add_keys_obj(child)
obj.keys.push(...child.keys)
})
}
function add_keys(arr) {
arr.forEach(function(obj, key) {
add_keys_obj(obj);
})
return arr;
}
add_keys(nestArray)
document.querySelector("pre").innerText = JSON.stringify(nestArray, null, 2);
<pre></pre>

Related

How to get all the 'items' data from this list?

I have a list of data like below:
The data consist of 6 categories (bags, shoes, girls, bags, and boys) and all of them store the same data (id, items(array: desc, id, imageUrl, name, price), routeName, title).
Now, I want to loop through all categories and just retrieve their name from the 'items', how can I do this?
I have tried the code below and I just can retrieve the name from one of the categories. Are there any ways to get all of the names from all collections? Thanks for helping me.
const { bags, shoes, girls, bags, boys } = collections;
const {items} = bags;
console.log(items.map(item => item.name));
I used this sample data object which mapping with your example. using below array functions can get the result more efficiently.
const data = {
bags: {
id: 1,
items: [
{ desc: "abc", id: 1, imageUrl: "url", name: "Rabbit Crossbody", price:"$10" },
{ desc: "qwe", id: 2, imageUrl: "url", name: "Cat Crossbody", price:"$10" },
{ desc: "pqr", id: 3, imageUrl: "url", name: "Parrot Crossbody", price:"$10" },
],
routeName:"bags",
title:"Bags"
},
boys: {
id: 1,
items: [
{ desc: "abc", id: 1, imageUrl: "url", name: "Lion Crossbody", price:"$10" },
{ desc: "qwe", id: 2, imageUrl: "url", name: "Dog Crossbody", price:"$10" },
{ desc: "pqr", id: 3, imageUrl: "url", name: "Crow Crossbody", price:"$10" },
],
routeName:"boys",
title:"Boys"
},
girls: {
id: 1,
items: [
{ desc: "abc", id: 1, imageUrl: "url", name: "Pig Crossbody", price:"$10" },
{ desc: "qwe", id: 2, imageUrl: "url", name: "Hen Crossbody", price:"$10" },
{ desc: "pqr", id: 3, imageUrl: "url", name: "fish Crossbody", price:"$10" },
],
routeName:"girls",
title:"Girls"
}};
const categories = Object.keys(data); // extract categories
const filteredNames = categories.map(category => {
const items = data[category].items; // extract items of each category
return items.map(item => item.name); // extract names of items into new[]
});
console.log('names', filteredNames.flat());
// ["Rabbit Crossbody", "Cat Crossbody", "Parrot Crossbody", "Lion Crossbody", "Dog Crossbody", "Crow Crossbody", "Pig Crossbody", "Hen Crossbody", "fish Crossbody"]
Try 2 loops:
const allNames = [];
for (const collection of Object.values(collections)) {
for(const item of collection.items) {
allNames.push(item.name);
}
}
console.log(allNames);
It's possible to do this with array functions too, but a loop is often the most obvious.

Javascript group by array of objects (complicated objects)

After a lot of tries and search, I couldn't solve my following problem:
I have the following array
[
{text: "text", title: "title", cid: "cid", active: true, nodes: [
{title: "subTitle", text: "subText", cid: "cid", active: true, nodes: [
{text:"123", title:"321"},
{text:"456", title:"654"},
{text:"789", title:"765"}
]},
{title: "subTitle", text: "subText2", cid: "cid2", active: true, nodes: [
{text:"testText", title:"testTitle1"},
{text:"testText", title:"testTitle2"},
{text:"testText", title:"testTitle3"}
]},
{title: "subTitle", text: "subText3", cid: "cid3", active: true, nodes: [
{text:"ycycy", title:"asd"},
{text:"nyd", title:"yf"},
{text:"xfg", title:"qq"}
]},
{title: "anotherSubTitle", text: "subText4", cid: "cid4", active: true, nodes: [
{text:"fff", title:"hhh"},
{text:"xxx", title:"sss"},
{text:"hhh", title:"jjj"}
]}
]}
]
I want to reach the following format:
[
{text: "text", title: "title", cid: "cid", active: true, nodes: [
{title: "subTitle", text: "subText", cid: "cid", active: true, nodes: [
{text:"123", title:"321"},
{text:"456", title:"654"},
{text:"789", title:"765"},
{text:"testText", title:"testTitle1"},
{text:"testText", title:"testTitle1"},
{text:"testText", title:"testTitle1"},
{text:"ycycy", title:"asd"},
{text:"nyd", title:"yf"},
{text:"xfg", title:"qq"}
]},
{title: "anotherSubTitle", text: "subText4", cid: "cid4", active: true, nodes: [
{text:"fff", title:"hhh"},
{text:"xxx", title:"sss"},
{text:"hhh", title:"jjj"}
]}
]}
]
I tried array.reduce and to loop through the array but each time I got a wrong result...
Any suggestion plz?
You could take a nested grouping by a property for all levels.
const
groupBy = (array, key) => array.reduce((r, { nodes, ...o }) => {
let temp = r.find(q => q[key] === o[key]);
if (!temp) r.push(temp = o);
if (nodes) (temp.nodes ??= []).push(...groupBy(nodes, key));
return r;
}, []),
data = [{ text: "text", title: "title", cid: "cid", active: true, nodes: [{ title: "subTitle", text: "subText", cid: "cid", active: true, nodes: [{ text: "123", title: "321" }, { text: "456", title: "654" }, { text: "789", title: "765" }] }, { title: "subTitle", text: "subText2", cid: "cid2", active: true, nodes: [{ text: "testText", title: "testTitle1" }, { text: "testText", title: "testTitle2" }, { text: "testText", title: "testTitle3" }] }, { title: "subTitle", text: "subText3", cid: "cid3", active: true, nodes: [{ text: "ycycy", title: "asd" }, { text: "nyd", title: "yf" }, { text: "xfg", title: "qq" }] }] }],
result = groupBy(data, 'title');
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Merge several objects into one object and combine properties JavaScript

I have multiple objects inside an array, I want to merge all the objects that has the same id, but I want to combine all the values from one property as well (In this case, channels)
This is the code:
defaultArray = [
{
id: "FirstId",
name: "Some random name"
channels: [{
id: "Channel-ASD",
name: "Channel ASD"
}]
},
{
id: "FirstId",
name: "Some random name"
channels: [{
id: "Channel-QWE",
name: "Channel QWE"
}]
},
{
id: "SecondId",
name: "Some random name"
channels: [{
id: "Channel-QAZ",
name: "Channel QAZ"
}]
}
];
Expected output:
newArray = [
{
id: "FirstId",
name: "Some random name"
channels: [
{
id: "Channel-ASD",
name: "Channel ASD"
},
{
id: "Channel-QWE",
name: "Channel QWE"
}
]
},
{
id: "SecondId",
name: "Some random name"
channels: [{
id: "Channel-QAZ",
name: "Channel QAZ"
}]
}
]
You need to study about filter, map, reduce, some, ...
let defaultArray = [
{
id: "FirstId",
name: "Some random name",
channels: [{
id: "Channel-ASD",
name: "Channel ASD"
}]
},
{
id: "FirstId",
name: "Some random name",
channels: [{
id: "Channel-QWE",
name: "Channel QWE"
}]
},
{
id: "SecondId",
name: "Some random name",
channels: [{
id: "Channel-QAZ",
name: "Channel QAZ"
}]
}
];
let transformed= defaultArray.reduce((result, item, index, original)=>{
if (result.some(i=>i.id===item.id)) return result;
let channels = original.reduce((r,i)=>{
i.id===item.id && (r=[...r, ...i.channels]);
return r;
},[]);
result.push({
...item,
channels
});
return result;
},[]);
console.log(JSON.stringify(transformed,null,2));

create a pruned copy of tree in javascript

I'm trying to create a pruned version of the tree below where I have the source data/tree:
const treeData = [{
title: '0-0',
key: '0-0',
children: [{
title: '0-0-0',
key: '0-0-0',
children: [
{ title: '0-0-0-0', key: '0-0-0-0', children: [] },
{ title: '0-0-0-1', key: '0-0-0-1', children: [] },
{ title: '0-0-0-2', key: '0-0-0-2', children: [] },
],
}, {
title: '0-0-1',
key: '0-0-1',
children: [
{ title: '0-0-1-0', key: '0-0-1-0', children: [] },
{ title: '0-0-1-1', key: '0-0-1-1', children: [] },
{ title: '0-0-1-2', key: '0-0-1-2', children: [] },
],
}, {
title: '0-0-2',
key: '0-0-2',
children: []
}],
}, {
title: '0-1',
key: '0-1',
children: [
{ title: '0-1-0-0', key: '0-1-0-0', children: [] },
{ title: '0-1-0-1', key: '0-1-0-1', children: [] },
{ title: '0-1-0-2', key: '0-1-0-2', children: [] },
],
}, {
title: '0-2',
key: '0-2',
children: []
}];
and an array of leaf nodes as inputs.
const leafNodes = ['0-0-1-2', '0-1-0-1', '0-1-0-2']
Given this input, I would want this pruned tree that uses the leaf nodes to build all paths from the root to each leaf:
const pruned [{
title: '0-0',
key: '0-0',
children: [{
title: '0-0-1',
key: '0-0-1',
children: [
{ title: '0-0-1-2',
key: '0-0-1-2',
children: []
}
]
}]
}, {
title: '0-1',
key: '0-1',
children: [{
title: '0-1-0-1',
key: '0-1-0-1',
children: []
}, {
title: '0-1-0-2',
key: '0-1-0-2',
children: []
}]
}]
I was thinking of building the copy node by node instead of copying the data source and then taking away the paths not buildable based on the array/list of leaf nodes as I figured that would be the easiest to grok for maintainability purposes but even then, I'm puzzled as to how to coordinate the process, especially when accounting for the middle nodes that have already been added to my copy tree in progress as would be the case for '0-1-0-1' and '0-1-0-2'. At any rate, I've been stumped for awhile and threw my hands up. The code referenced is javascript but I'd be open to answers in other languages similar enough to javascript.
You could build new array/objects by finding the target key and collect all objects to it by returning the arrays with the necessary nodes.
function getParts(array, leafes) {
var result = [];
array.forEach(o => {
var children;
if (leafes.includes(o.key)) {
result.push(o);
return;
}
children = getParts(o.children, leafes);
if (children.length) {
result.push(Object.assign({}, o, { children }));
}
});
return result;
}
const
treeData = [{ title: '0-0', key: '0-0', children: [{ title: '0-0-0', key: '0-0-0', children: [{ title: '0-0-0-0', key: '0-0-0-0', children: [] }, { title: '0-0-0-1', key: '0-0-0-1', children: [] }, { title: '0-0-0-2', key: '0-0-0-2', children: [] }] }, { title: '0-0-1', key: '0-0-1', children: [{ title: '0-0-1-0', key: '0-0-1-0', children: [] }, { title: '0-0-1-1', key: '0-0-1-1', children: [] }, { title: '0-0-1-2', key: '0-0-1-2', children: [] }] }, { title: '0-0-2', key: '0-0-2', children: [] }] }, { title: '0-1', key: '0-1', children: [{ title: '0-1-0-0', key: '0-1-0-0', children: [] }, { title: '0-1-0-1', key: '0-1-0-1', children: [] }, { title: '0-1-0-2', key: '0-1-0-2', children: [] }] }, { title: '0-2', key: '0-2', children: [] }],
leafNodes = ['0-0-1-2', '0-1-0-1', '0-1-0-2'],
pruned = getParts(treeData, leafNodes);
console.log(pruned);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Javascript update an object with a new object where value matches value in array of objects

I have an array of objects with an unlimited number of nested children objects like this:
var items = [
{
id: 1,
name: 'Item 1',
children: [
{
id: 2,
name: 'Item 2',
children: [],
}
]
},
{
id: 2,
name: 'Item 2',
children: [],
},
{
id: 3,
name: 'Item 3',
children: [
{
id: 1,
name: 'Item 1',
children: [
id: 2,
name: 'Item 2',
children: [],
]
}
]
}
];
I want to update every occurrence of object with id 2 to have a name of "This is a cool object".
I'm not sure if this needs recursion or if some type of find and replace will do.
var newObject = {
id: 2,
name: 'This is a cool object',
children: []
}
I want to replace every occurrence of any object with id 2 with the new object (regardless of the rest of the objects contents).
I tried looping through but then realized that it could potentially be unlimited levels deep.
With recursion.
var items = [ { "id": 1, "name": "Item 1", "children": [ { "id": 2, "name": "Item 2", "children": [] } ] }, { "id": 2, "name": "Item 2", "children": [] }, { "id": 3, "name": "Item 3", "children": [ { "id": 1, "name": "Item 1", "children": [{ "id": 2, "name": "Item 2", "children": [] }] } ] }];
function loop(array, targetId) {
array.forEach((e) => {
if(e.id === targetId) {
e.name = 'This is a cool object';
}
if (e.children && e.children.length > 0) {
loop(e.children, targetId);
}
});
}
loop(items, 2);
console.log(items);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Try this single line of code with Array filter() method.
function findID(items) {
return items.filter(obj => (obj.id === 2)?obj.name='This is a cool object':findID(obj.children));
}
var res = findID(items);
Working Demo
var items = [
{
id: 1,
name: 'Item 1',
children: [
{
id: 2,
name: 'Item 2',
children: [],
}
]
},
{
id: 2,
name: 'Item 2',
children: [],
},
{
id: 3,
name: 'Item 3',
children: [
{
id: 1,
name: 'Item 1',
children: [
{
id: 2,
name: 'Item 2',
children: [],
}
]
}
]
}
];
function findID(items) {
return items.filter(obj => (obj.id === 2)?obj.name='This is a cool object':findID(obj.children));
}
console.log(findID(items));

Categories

Resources