I'm receiving a JSON from a Laravel API in this way:
[
{
"id":48,
"parentid":0,
"title":"Item 1",
"child_content":[
{
"id":49,
"parentid":48,
"title":"Itema 1.1",
},
{
"id":52,
"parentid":48,
"title":"Item 1.2",
}
]
},
{
"id":58,
"parentid":0,
"title":"Item 2",
"child_content":[
{
"id":59,
"parentid":58,
"title":"Itema 2.1",
},
{
"id":60,
"parentid":58,
"title":"Item 2.2",
}
]
}
]
and what I need is change the JSON into this:
{
"data":
[
{
"data":
{
"id":68,
"parentid":0,
"title":"Item 1"
},
"children":
[
{
"data":
{
"id":69,
"parentid":68,
"title":"Item 1.1"
},
},
{
"data":
{
"id":69,
"parentid":68,
"title":"Item 1.2"
}
}
]
}
]
}
I've been dealing with this... but I'm not able to find the way to do this properly...
How can I do this in PHP or Javascript / TypeScript (Angular 2).
Thank you in advance.
This should achieve your goal. Basically I'm just grabbing child_content, renaming it to children and copying the 3 other attributes. The children.map iteration is putting the existing data inside an object with a key of data:
const input = [{"id":48,"parentid":0,"title":"Item 1","child_content":[{"id":49,"parentid":48,"title":"Itema 1.1"},{"id":52,"parentid":48,"title":"Item 1.2"}]},{"id":58,"parentid":0,"title":"Item 2","child_content":[{"id":59,"parentid":58,"title":"Itema 2.1"},{"id":60,"parentid":58,"title":"Item 2.2"}]}]
const output = {
data: input.map((data) => {
const {
child_content: children,
id,
parentId,
title,
} = data;
return {
id,
parentId,
title,
children: children.map(data => ({data})),
};
})
}
console.log(output);
You can use JavaScript Array.prototype.map():
var json = [{"id": 48,"parentid": 0,"title": "Item 1","child_content": [{"id": 49,"parentid": 48,"title": "Itema 1.1",}, {"id": 52,"parentid": 48,"title": "Item 1.2",}]}, {"id": 58,"parentid": 0,"title": "Item 2","child_content": [{"id": 59,"parentid": 58,"title": "Itema 2.1",}, {"id": 60,"parentid": 58,"title": "Item 2.2",}]}],
result = {
data: json.map(function (item) {
return {
data: {
id: item.id,
parentid: item.parentid,
title: item.title
},
children: item.child_content.map(function (childItem) {
return {
data: {
id: childItem.id,
parentid: childItem.parentid,
title: childItem.title
}
}
})
};
})
};
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Assuming that the differences in the two datasets are due to copy/paste from different datasets, you can use the array map method to transform the data for you.
map works by iterating through each item of an array, and allows you to return a new item in the shape which you'd like.
var input = [{"id":48,"parentid":0,"title":"Item 1","child_content":[{"id":49,"parentid":48,"title":"Itema 1.1"},{"id":52,"parentid":48,"title":"Item 1.2"}]},{"id":58,"parentid":0,"title":"Item 2","child_content":[{"id":59,"parentid":58,"title":"Itema 2.1"},{"id":60,"parentid":58,"title":"Item 2.2"}]}];
var output = {
data: input.map(function(parent) {
// return a new object which contains the properties which you need
return {
data: {
id: parent.id,
parentid: parent.parentid,
title: parent.title
},
// children is an array, so we can use map again to transform them
children: parent.child_content.map(function(child) {
return {
data: {
id: child.id,
parentid: parent.id,
title: child.title
}
};
})
}
})
}
console.log(output);
You could convert the structure without mutating the original object with iterating and recursive calls of the convert function.
It works for any depth.
function convert(o) {
var temp = { data: {} };
Object.keys(o).forEach(function (k) {
if (k === 'child_content') {
temp.children = o[k].map(convert);
} else {
temp.data[k] = o[k];
}
});
return temp;
}
var data = [{ id: 48, parentid: 0, title: "Item 1", child_content: [{ id: 49, parentid: 48, title: "Itema 1.1" }, { id: 52, parentid: 48, title: "Item 1.2" }] }, { id: 58, parentid: 0, title: "Item 2", child_content: [{ id: 59, parentid: 58, title: "Itema 2.1" }, { id: 60, parentid: 58, title: "Item 2.2" }] }],
result = { data: data.map(convert) };
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Ok, what I finally did is maybe not too much elegant... but it works, probably I should create a recursive function to manage different nested levels. I took the #RobM solution and replicated the children functionality at the second level like this:
convert(input)
{
const output =
{
data: input.map((data) =>
{
const { children: children } = data;
delete data.children;
return {
data,
children: children.map(data =>
{
const { children: children } = data;
delete data.children;
return {
data,
children: children.map(data => ({data})),
};
}),
};
})
}
return output.data;
}
Related
I have a menu with this structure
item1
item2
childrenOfItem2
childrenOfchildren1
childrenOfchildren2
HELLOchildrenOfchildren3
childrenOfItem2
childrenOfItem2
HELLOitem3
item4
childrenOfItem4
HELLOchildrenOfItem4
item5
childrenOfItem5
So, Id' like to get all the items that have the word "HELLO" and what I'm doing is a loop over the first items, then, another loop and then another loop, is there any other way of doing it? Since let's say that if we add another level of depth in the menu it will not work,
Thank you!
Edited: adding JS for better understanding
const matchName = (item, word) =>
item?.title?.toLowerCase()?.includes(word?.toLowerCase());
const filter = (word = "", arr = []) => {
const listOfItems = [];
arr.forEach((item) => {
if (matchName(item, word)) {
listOfItems.push(item);
} else if (item?.children?.length > 0) {
const newSubItem = [];
item.children.forEach((subItem) => {
if (matchName(subItem, word)) {
newSubItem.push(subItem);
} else if (subItem?.children?.length > 0) {
const newSubSubItems = [];
subItem.children.forEach((subsubItem) => {
if (matchName(subsubItem, word)) {
newSubSubItems.push(subsubItem);
}
});
if (newSubSubItems?.length > 0) {
newSubItem.push({ ...subItem, children: newSubSubItems });
}
}
});
if (newSubItem?.length > 0) {
listOfItems.push({ ...item, children: newSubItem });
}
}
});
return listOfItems;
};
Sample of arr received as parameter in the fnc:
const list = [
{
id: "41",
title: "sample",
children: [
{
id: "42",
title: "sample",
children: [
{
id: "43",
title: "sample",
children: [],
},
{
id: "44",
title: "sample",
children: [],
},
{
id: "45",
title: "sample",
children: [],
},
],
},
{
id: "46",
title: "sample",
children: [
{
id: "47",
title: "sample",
children: [],
},
{
id: "48",
title: "sample",
children: [],
},
],
},
],
},
{
id: "29",
title: "sample",
children: [
{
id: "30",
title: "sample",
children: [],
},
{
id: "49",
title: "sample",
children: [],
},
{
id: "31",
title: "sample",
children: [],
},
],
},
];
If you really don't want to change your structure and flatten your list, you can loop them dynamically, just use a function and call it within itself.
Here's an example using the const list you provided:
let found = false,
loop = (items, filter) => {
found = false;
let results = items.filter(a => filter(a));
if(results.length > 0) {
found = true;
return results;
}
if(!found && items && items.length > 0) {
for(let i = 0; i < items.length && !found; i++) {
if(items[i] && items[i].children && items[i].children.length > 0)
results = loop(items[i].children, filter);
}
}
return results;
};
let items = loop(list, item => item.id && item.id == "48");
In the example above we filter the list dynamically, iterating each and every item in the list and return the first item we find that matches a provided filter (2nd parameter).
You can use this to do pretty much whatever you'd like and can add arguments to add the menu "level" you're currently on, the parents, etc.
Note that this might get a bit slow if the list goes very deep, wrote it quickly and didn't tested outside of your provided list.
Ideally I would change the structure in order to make it easier to work with but if you must keep it this way, this works.
You could find the object (without children) and get a flat array.
const
find = (array, title) => array.flatMap(({ children, ...o }) => [
...(o.title.includes(title) ? [o]: []),
...find(children, title)
]),
list = [{ id: "41", title: "sample", children: [{ id: "42", title: "sample", children: [{ id: "43", title: "sample", children: [] }, { id: "44", title: "sample", children: [] }, { id: "45", title: "sample", children: [] }] }, { id: "46", title: "no sample", children: [{ id: "47", title: "sample", children: [] }, { id: "48", title: "sample", children: [] }] }] }, { id: "29", title: "sample", children: [{ id: "30", title: "sample", children: [] }, { id: "49", title: "no sample", children: [] }, { id: "31", title: "sample", children: [] }] }];
console.log(find(list, 'no sample'));
console.log(find(list, 'ample'));
.as-console-wrapper { max-height: 100% !important; top: 0; }
I am trying to achieve an operation where Ι am not sure where to start with.
Down below is my initial object:
[
{ type: "1", name: "Anthony" },
{
type: "1",
name: "Linus",
},
{
type: "2",
name: "Sebastin",
},
]
What I am trying to achieve is to move the objects which has same type inside an array and have a key value, named title and string type.
I am trying to produce an output equivalent to this and I am not sure where to begin with. Any help will be useful and appreciated. Thanks in advance <3
[
{
title: "1",
sub_items: [
{
type: "1",
name: "Anthony",
},
{
type: "1",
name: "Linus",
},
],
},
{
type: "2",
name: "Sebastin",
},
];
You can use Array.reduce() to group the input items by type/title and create the desired output:
const input = [{ "type":"1", "name":"Anthony" }, { "type": "1", "name": "Linus" }, { "type":"2", "name":"Sebastin" }]
const result = Object.values(input.reduce((acc, { type, name }) => {
acc[type] = acc[type] || { title: type, sub_items: [] };
acc[type].sub_items.push({ type, name });
return acc;
}, {}));
console.log('Result:', result)
.as-console-wrapper { max-height: 100% !important; }
You can make it with Array.reduce
const data = [
{ type: "1", name: "Anthony" },
{
type: "1",
name: "Linus",
},
{
type: "2",
name: "Sebastin",
},
];
const output = data.reduce((acc, curr, index, list) => {
const matchNodes = list.filter((node) => node.type === curr.type);
if (matchNodes.length > 1) {
const accNode = acc.find((item) => item.title === curr.type);
if (accNode) {
accNode.sub_items.push(curr);
} else {
acc.push({
title: curr.type,
sub_items: [curr],
});
}
} else {
acc.push(curr);
}
return acc;
}, []);
console.log(output);
Is there a way good way JS/ES6 to loop through an object and it's children and creating new object tree array.
I have this json tree object:
[
{
id: "001",
deparmentsIds: [
"002",
"003"
],
details: {
parentDeparmentsId: null,
name: "Top"
}
},
{
id: "002",
deparmentsIds:[
"004"
],
details: {
parentDeparmentsId: ["001"],
name: "Operations"
}
},
{
id: "003",
deparmentsIds:[]
details: {
parentDeparmentsId: ["001"],
name: "Support"
}
},
{
id: "004",
deparmentsIds:[]
details: {
parentDeparmentsId: ["002"],
name: "Support operations"
}
}
]
I want to create new object array tree that looks like this:
You could create recursive function with reduce and map method to create nested object structure.
const data = [{"id":"001","deparmentsIds":["002","003"],"details":{"parentDeparmentsId":null,"name":"Top"}},{"id":"002","deparmentsIds":["004"],"details":{"parentDeparmentsId":"001","name":"Operations"}},{"id":"003","deparmentsIds":[],"details":{"parentDeparmentsId":"001","name":"Support"}},{"id":"004","deparmentsIds":[],"details":{"parentDeparmentsId":"002","name":"Support operations"}}]
function tree(input, parentId) {
return input.reduce((r, e) => {
if (e.id == parentId || parentId == undefined && e.details.parentDeparmentsId == null) {
const children = [].concat(...e.deparmentsIds.map(id => tree(input, id)))
const obj = {
[e.details.name]: children
}
r.push(obj)
}
return r;
}, [])
}
const result = tree(data)
console.log(result)
You could collect all information in an object with a single loop and return only the nodes with no parent.
function getTree(data, root) {
var o = {};
data.forEach(({ id, details: { parentDeparmentsId: parent, name } }) => {
var temp = { id, name };
if (o[id] && o[id].children) {
temp.children = o[id].children;
}
o[id] = temp;
o[parent] = o[parent] || {};
o[parent].children = o[parent].children || [];
o[parent].children.push(temp);
});
return o[root].children;
}
var data = [{ id: "001", deparmentsIds: ["002", "003"], details: { parentDeparmentsId: null, name: "Top" } }, { id: "002", deparmentsIds: ["004"], details: { parentDeparmentsId: ["001"], name: "Operations" } }, { id: "003", deparmentsIds: [], details: { parentDeparmentsId: ["001"], name: "Support" } }, { id: "004", deparmentsIds: [], details: { parentDeparmentsId: ["002"], name: "Support operations" } }],
tree = getTree(data, null);
console.log(tree);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Currently I have two different array of objects and my end result is I am trying to have one single array of objects.
const postIds = [
{ id: 4938960132 },
{ id: 5586491011 },
{ id: 4671908225 },
{ id: 4594788047 },
{ id: 4657970305 }
]
const images = [
{ featuredImage: 'https://www.rusticfurnitureboston.com/hubfs/Blog_Media/rustic20coffee20table.jpeg%3Ft=1528912781831-6.jpeg' },
{ featuredImage: 'https://www.rusticfurnitureboston.com/hubfs/Blog_Media/Amazing-Table-For-Flamboyant-Furniture-Home-Design-Ideas-With-Rustic-Furniture-Coffee-Table.jpg%3Ft=1528912781831-6.jpeg' },
{ featuredImage: 'https://www.rusticfurnitureboston.com/hubfs/Blog_Media/envoy-lookout-rooftop-11b-780x520.jpg%3Ft=1528912781831-6.jpeg' },
{ featuredImage: 'https://www.rusticfurnitureboston.com/hubfs/Blog_Media/Alexanderplatz_03.jpg%3Ft=1528912781831-6.jpeg' },
{ featuredImage: 'https://www.rusticfurnitureboston.com/hubfs/Blog_Media/mountain-landscape-wallpaper-29048-29765-hd-wallpapers.jpg%3Ft=1528912781831-6.jpeg' }
]
What I am hoping to have at the end is a data structure like the following
const newData = [
{ id: 4938960132, featuredImage: 'https://www.rusticfurnitureboston.com/hubfs/Blog_Media/rustic20coffee20table.jpeg%3Ft=1528912781831-6.jpeg' },
{ id: 5586491011, featuredImage: 'https://www.rusticfurnitureboston.com/hubfs/Blog_Media/Amazing-Table-For-Flamboyant-Furniture-Home-Design-Ideas-With-Rustic-Furniture-Coffee-Table.jpg%3Ft=1528912781831-6.jpeg' },
{ id: 4671908225, featuredImage: 'https://www.rusticfurnitureboston.com/hubfs/Blog_Media/envoy-lookout-rooftop-11b-780x520.jpg%3Ft=1528912781831-6.jpeg' },
{ id: 4594788047, featuredImage: 'https://www.rusticfurnitureboston.com/hubfs/Blog_Media/mountain-landscape-wallpaper-29048-29765-hd-wallpapers.jpg%3Ft=1528912781831-6.jpeg'},
{ id: 4657970305, featuredImage: 'https://www.rusticfurnitureboston.com/hubfs/Blog_Media/mountain-landscape-wallpaper-29048-29765-hd-wallpapers.jpg%3Ft=1528912781831-6.jpeg' }
]
I've been trying a lot of different things here such as reduce, spread operator and other es6 functions but cannot seem to get the data structure that I am looking for.
Any help would be much appreciated
Assuming the two arrays have the same length:
const newData = [...postIds.map((postId, i) => Object.assign({}, postId, images[i]))];
Alternativelly, with ... operator:
const newData = [...postIds.map((item, i) => {
return {
...item,
...images[i]
};
})];
Working snippet:
const postIds = [
{ id: 4938960132 },
{ id: 5586491011 },
{ id: 4671908225 },
{ id: 4594788047 },
{ id: 4657970305 }
]
const images = [
{ featuredImage: 'https://www.rusticfurnitureboston.com/hubfs/Blog_Media/rustic20coffee20table.jpeg%3Ft=1528912781831-6.jpeg' },
{ featuredImage: 'https://www.rusticfurnitureboston.com/hubfs/Blog_Media/Amazing-Table-For-Flamboyant-Furniture-Home-Design-Ideas-With-Rustic-Furniture-Coffee-Table.jpg%3Ft=1528912781831-6.jpeg' },
{ featuredImage: 'https://www.rusticfurnitureboston.com/hubfs/Blog_Media/envoy-lookout-rooftop-11b-780x520.jpg%3Ft=1528912781831-6.jpeg' },
{ featuredImage: 'https://www.rusticfurnitureboston.com/hubfs/Blog_Media/Alexanderplatz_03.jpg%3Ft=1528912781831-6.jpeg' },
{ featuredImage: 'https://www.rusticfurnitureboston.com/hubfs/Blog_Media/mountain-landscape-wallpaper-29048-29765-hd-wallpapers.jpg%3Ft=1528912781831-6.jpeg' }
]
const newData = [...postIds.map((item, i) => Object.assign({}, item, images[i]))];
console.log(newData)
You creduce both array by mapping the objects at the same index and by assigning to a new object.
This works for an arbitrary count of arrays.
const
postIds = [{ id: 4938960132 }, { id: 5586491011 }, { id: 4671908225 }, { id: 4594788047 }, { id: 4657970305 }],
images = [{ featuredImage: 'https://www.rusticfurnitureboston.com/hubfs/Blog_Media/rustic20coffee20table.jpeg%3Ft=1528912781831-6.jpeg' }, { featuredImage: 'https://www.rusticfurnitureboston.com/hubfs/Blog_Media/Amazing-Table-For-Flamboyant-Furniture-Home-Design-Ideas-With-Rustic-Furniture-Coffee-Table.jpg%3Ft=1528912781831-6.jpeg' }, { featuredImage: 'https://www.rusticfurnitureboston.com/hubfs/Blog_Media/envoy-lookout-rooftop-11b-780x520.jpg%3Ft=1528912781831-6.jpeg' }, { featuredImage: 'https://www.rusticfurnitureboston.com/hubfs/Blog_Media/Alexanderplatz_03.jpg%3Ft=1528912781831-6.jpeg' }, { featuredImage: 'https://www.rusticfurnitureboston.com/hubfs/Blog_Media/mountain-landscape-wallpaper-29048-29765-hd-wallpapers.jpg%3Ft=1528912781831-6.jpeg' }],
result = [images, postIds].reduce(
(r, a) => a.map((o, i) => Object.assign({}, o, r[i])),
[]
);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
I am using text to search over some data. Some of the data is nested, and the other part is not.
If it returns a result from the nested part of the data, it should only return that nested value (and it's parent value).
I've put it on codeSandbox too- https://codesandbox.io/s/Q0w16jLP0
The function:
function filterTypes(items, search) {
return items.filter(items => {
const nestedName = items.typechild.map(x => x.name.toLowerCase());
const name = items.name.toLowerCase();
return search.every(x => {
return name.includes(x) || nestedName.some(v => v.includes(x));
});
});
}
The datastructure:
{
"name": "Food",
"typechild": [{
"name": "Fruit", "id":12,
}, {
"name": "Vegetable", "id":13,
}]
}
How it works now:
it returns all the children of Food.
Desired result:
If the filter has value Fruit, it should return...
Food as the title
Fruit below that title
Desired datastructure
{
"name": "Food",
"typechild": [{
"name": "Fruit", "id":12,
}]
}
I would use a recursive approach:
function check(el,name){
if(el.name.includes(name)){
return [el];
}
if(el.typechild && el.typechild.find){
var child=el.typechild.find(function(child){
return check(child,name);
});
}
if(child){
child=check(child,name);
child.unshift(el);
return child;
}
return false;
}
It will return
[Food Object, Fruit Object]
Solution with mutating the the original data.
var data = [{ name: "Food", typechild: [{ name: "Fruit", level: 2, color: "#fff" }, { name: "Vegetable", level: 2, color: "#fff" }] }, { name: "Entertainment", typechild: [{ name: "Book", level: 2, color: "#fff" }, { name: "Movie", level: 2, color: "#fff" }, { name: "Bar", level: 3, color: "#fff" }] }, { name: "Misc", typechild: [{ name: "Foo", level: 2, color: "#fff" }] }],
search = 'Fruit',
index = data.length,
temp;
while (index--) {
temp = data[index].typechild.filter(o => o.name === search);
if (temp.length) {
data[index].typechild = temp;
} else {
data.splice(index, 1);
}
}
console.log(data);
.as-console-wrapper { max-height: 100% !important; top: 0; }