I have object like this:
{
"id": 1,
"name": "first",
"sections": [
{
"id": 1,
"title": "First section",
"contents": [
{
"id": "123",
"title": "Sample title 1",
"description": "<html>code</html>",
},
{
"id": "124",
"title": "Sample title 2",
"description": "<html>code</html>"
},
{
"id": "125",
"title": "Some other sample",
"description": "<html>code</html>"
}
]
},
{
"id": 2,
"title": "Second section",
"contents": [
{
"id": "126",
"title": "Sample title 126",
"description": "<html>code</html>"
},
{
"id": "127",
"title": "Sample title 127",
"description": "<html>code</html>"
}
]
}
]
}
I want to remove specific object from contents array by its id (all those ids are unique).
I can easily find element I want to remove, but I'm unable to find in which section this element is to splice it later.
obj.sections.forEach(function(section) {
section.contents.forEach((content) => {
if (content.id == 125) {
console.log(content)
console.log(section)
}
})
})
In above code console.log(sections) returns undefined. How can I get position in sections array which contains contents array that has specific id. For example, id: 125 would return sections position 0, so I can use splice to remove that element.
If my approach is completely wrong please point me in right direction, thanks :)
You could use .filter() instead of .splice(). .filter() will keep all items which you return true for and discard of those which you return false for. So, if the current section's content's object has an id equal to the one you want to remove you can return false to remove it, otherwise return true to keep that item. You can use this with .map() to map each section object to a new one with an updated contents array:
const obj = { "id": 1, "name": "first", "sections": [ { "id": 1, "title": "First section", "contents": [ { "id": "123", "title": "Sample title 1", "description": "<html>code</html>", }, { "id": "124", "title": "Sample title 2", "description": "<html>code</html>" }, { "id": "125", "title": "Some other sample", "description": "<html>code</html>" } ] }, { "id": 2, "title": "Second section", "contents": [ { "id": "126", "title": "Sample title 126", "description": "<html>code</html>" }, { "id": "127", "title": "Sample title 127", "description": "<html>code</html>" } ] } ] };
const idToRemove = 125;
obj.sections = obj.sections.map(
sec => ({...sec, contents: sec.contents.filter(({id}) => id != idToRemove)})
);
console.log(obj);
.as-console-wrapper { max-height: 100% !important; top: 0; } /* ignore */
Why not use a simple filter for accomplishing the task. Also, it will be much cleaner and immutable way of doing it.
let newArrWithoutContentWithGivenId = obj.sections.map(section =>
({...section, contents: section.contents.filter(content =>
content.id != 125)}));
Here, we are mapping each section whose content does not contain the ID 125. In short, section > content.id != 125 will be removed from the new array.
Hope it helps :)
Note: Code is not tested, it is just to help you find a way to do it cleanly.
You only need to use the second argument of forEach :)
obj.sections.forEach(function(section, sectionIndex) {
section.contents.forEach((content, contentIndex) => {
if (content.id == 125) {
// use the sectionIndex and contentIndex to remove
}
})
})
Related
Let's say we have some houses represented as JSON. Something like this:
[
{
"id": "1",
"code": "1",
"name": "Smith's",
"children": [
{
"id": "",
"code": "11",
"name": "Kitchen",
"children": [
{
"id": "",
"code": "111",
"name": "Sink",
"children": []
}
]
},
{
"id": "",
"code": "12",
"name": "Living Room",
"children": [
{
"id": "",
"code": "121",
"name": "Television",
"children": [
{
"id": "",
"code": "1211",
"name": "Panel buttons",
"children": [
{
"id": "",
"code": "12111",
"name": "Power button",
"children": []
},
{
"id": "",
"code": "12112",
"name": "Colors adjust button",
"children": []
}
]
},
{
"id": "",
"code": "1221",
"name": "Screen",
"children": []
}
]
}
]
}
]
},
{
"id": "2",
"code": "2",
"name": "Taylor's",
"children": [
// Here goes all house places and items like the example above
]
},
{
"id": "1",
"code": "1",
"name": "Wilson's",
"children": [
// Here goes all house places and items like the example above
]
}
]
Take notice that the "code" property, found in each item, is something to represent the "path" until that item, carrying its parents "code" property concatenated with its own position by incremental order. So the code "11" means house 1 and child 1. And 212 would be house 2, child 1, child 2. Also take notice that all items follow the same type. In other words, every item has a children that follows its own type. So, it could be infinite.
Now, I'd like to maintain these structure. Adding items, updating items and so on. Let's say we want to add a carpet in Smith's living room. We would go deep in the structure 2 levels, which are Smith's house (index 0 of the array) and living room (index 1 of the children array). And then add a carpet.
The problem is it won't be 2 levels in all cases. What if I wanted to add a bathroom? It would be level 1, alongside with kitchen in living room (the first children). What if I'd like to add a microwave in the kitchen and add to it buttons, display, etc?
I think I'm a recursive scenario where I have to visit all items and, if it is the one I'm looking to reach at, add/updated it.
I've tried following this example
I couldn't figure it out how to bring it to my case. though.
I appreciate if your contribution is in JavaScript, but feel free to represent it in other language in case you are better in other language =).
There are indeed some questions, like for instance what happens if you have more than 10 items as child and why do you need it?
And what happens if you remove any item on any level? will you recursively start updating all codes?
Nevertheless I gave it a go. In essence what I do in the code is first search for the parent (example: Kitchen) where you want to add it to and then add the new child item (example: Carpet) to it.
The search is a typical recursive search.
The child addition is a typical addition to an array.
For argument's sake I assumed that the fields code always exist and that children is always an array.
// Actual code is underneath the declaration of this array
let houseList = [
{
"id": "1",
"code": "1",
"name": "Smith's",
"children": [
{
"id": "",
"code": "11",
"name": "Kitchen",
"children": [
{
"id": "",
"code": "111",
"name": "Sink",
"children": []
}
]
},
{
"id": "",
"code": "12",
"name": "Living Room",
"children": [
{
"id": "",
"code": "121",
"name": "Television",
"children": [
{
"id": "",
"code": "1211",
"name": "Panel buttons",
"children": [
{
"id": "",
"code": "12111",
"name": "Power button",
"children": []
},
{
"id": "",
"code": "12112",
"name": "Colors adjust button",
"children": []
}
]
},
{
"id": "",
"code": "1221",
"name": "Screen",
"children": []
}
]
}
]
}
]
},
{
"id": "2",
"code": "2",
"name": "Taylor's",
"children": [
// Here goes all house places and items like the example above
]
},
{
"id": "1",
"code": "1",
"name": "Wilson's",
"children": [
// Here goes all house places and items like the example above
]
}
]
addChild(houseList,"11",{name:"Carpet" });
addChild(houseList,"1211",{name: "Volume Up Button"});
addChild(houseList,"1211",{name: "Volume Down Button"});
console.log('new houselist', houseList);
// child is just what you want to add and the parentCode refers to where you want to add it to
function addChild(houseList, parentCode, child) {
let parent = findInHouseList(houseList,parentCode,child);
let amountOfChildren = parent.children.length;
let newCodeName = parentCode +""+ (amountOfChildren+1);
child = {...{id: "", code: newCodeName, children: []}, ...child};
console.log('adding child ', child);
parent.children = [...parent.children, child];
}
function findInHouseList(houseList,code) {
for (let house of houseList) {
let foundElement = findElement(house,code);
if ( foundElement)
return foundElement;
}
}
function findElement(currentElement, code) {
if ( currentElement.code === code)
return currentElement;
if (currentElement.children?.length > 0)
{
for (let child of currentElement.children) {
let foundElement = findElement(child,code);
if ( foundElement)
return foundElement;
}
}
return null;
}
I decided to let the code manage the code names for new children. It seems the easiest.
What you're trying to do is updating a JSON value at a dynamic path.
This function will append a child to the item which holds the specified code.
You may add conditions to check if the item at the code is defined
function appendChild(houses, code, item) {
let path = code.split('')
let o = houses
for (let i = 0; i < path.length; i++) {
let n = path[i] - 1
o = o[n]["children"]
}
o.push(item)
return houses
}
However, you should start your code indexes at 0 and storing them inside the JSON is useless since they are simply the path to reach the item.
I'm a bit new with javascript. Is there a way to find all icon and get their value and then replace their values with a new one?
I need to replace all of fa/FaCopy with fcb/FcbCopy in the json payload below. Any libraries or functions you can share?
[
{
"section": "feature-list",
"data": {
"title": "Title here",
"body": "body here",
"features": [
{
"title": "Title here 1",
"image": "body here",
"icon": "fa/FaCopy"
},
{
"title": "Title here 2",
"image": "body here",
"icon": "fa/FaCopy"
},
{
"title": "Title here 3",
"image": "body here",
"icon": "fa/FaCopy"
}
]
}
},
{
"section": "title-list",
"data": {
"title": "Title here",
"titles": {
"list": [
{
"title": "Title here 1",
"icon": "fa/FaCopy"
},
{
"title": "Title here 2",
"icon": "fa/FaCopy"
},
{
"title": "Title here 3",
"icon": "fa/FaCopy"
}
]
}
}
}
]
This will be very customized solution and deeply depended on your data.
If your data type will not change and remain same then you could use/modify something like this.
// DATA = Your array
const result = DATA.map((list) => ({
...list,
data: {
...list.data,
features: list.data.features?.map((feature) => ({
...feature,
icon: feature.icon === 'fa/FaCopy' ? 'fcb/FcbCopy' : feature.icon
})),
},
}));
console.log(result); // your expected result
I'm trying to pull out the individual images in the "images" object below under "details" section.
Seem to just be getting nothing printing out. Looking for the correct way to pull within the details.images.image1,2, or 3.
Here is the JSON data I'm working with so far:
{
"books": [
{
"title": "title 1",
"image": "/image1.jpg"
},
{
"title": "title 2",
"image": "/image2.jpg"
}
],
"details": [
{
"author": "book author",
"name": "Book name",
"price": 34.99,
"publisher": "Penguin Books",
"images": [
{
"image1": "/image1.jpg",
"image2": "/image2.jpg",
"image3": "/image3.jpg"
}
]
}
]
}
Also here is the JSON call I'm making in a Book component:
{staticdata.details.map(detail => (
<Book
book_name={detail.author}
book_price={detail.price}
image={detail.images.image1}
/>
))}
Here's an example of accessing those nested properties and logging them to the console. It appears your attempt was mostly correct, but images is an array.
const data = {
"books": [
{
"title": "title 1",
"image": "/image1.jpg"
},
{
"title": "title 2",
"image": "/image2.jpg"
}
],
"details": [
{
"author": "book author",
"name": "Book name",
"price": 34.99,
"publisher": "Penguin Books",
"images": [
{
"image1": "/image1.jpg",
"image2": "/image2.jpg",
"image3": "/image3.jpg"
}
]
}
]
}
data.details.map(detail => {
console.log(detail.author, detail.price, detail.images[0].image1);
});
I have a json and would like to filter for one key multiple attribites as exact match.
I tried the following:
let data = [{
"name": "Product 2",
"link": "/stock/product2",
"category": "234",
"description": ""
}, {
"name": "Product 1",
"link": "/stock/product1",
"category": "1231",
"description": ""
}, {
"name": "Product 3",
"link": null,
"category": "22",
"description": ""
}]
data = data.filter(cv => cv.category === ["22", "234"]);
console.log(JSON.stringify(data))
I would like to get back the object with the name: Product 2 and name: Product 3.
Any suggestions why I get [] back?
I appreciate your replies!
Consider using Set.has for your desired attributes, so you can can have O(1) lookup time rather than the O(n) (where n is the number of desired attributes) lookup time using Array.includes.
As a result, if you use a set the overall the time complexity for the whole filter line will be O(m) (where m is the number of objects in data) rather than O(mn) if you used Array.includes or had multiple if-else / or conditions to check for each desired attribute:
const data = [
{
name: "Product 2",
link: "/stock/product2",
category: "234",
description: "",
},
{
name: "Product 1",
link: "/stock/product1",
category: "1231",
description: "",
},
{
name: "Product 3",
link: null,
category: "22",
description: "",
},
];
const desiredCategories = new Set(["22", "234"]);
const filteredData = data.filter(cv => desiredCategories.has(cv.category));
console.log(JSON.stringify(filteredData, null, 2));
You are comparing a single value against an array of values. One solution would be to check for one value or (||) the other.
data = data.filter(cv => cv.category === "22" || cv.category === "234");
This can be achieved by the includes method.
let data = [{
"name": "Product 2",
"link": "/stock/product2",
"category": "234",
"description": ""
}, {
"name": "Product 1",
"link": "/stock/product1",
"category": "1231",
"description": ""
}, {
"name": "Product 3",
"link": null,
"category": "22",
"description": ""
}]
data = data.filter(cv => ["22", "234"].includes(cv.category));
console.log(JSON.stringify(data))
Besides, I think this is easy to read/understand.
Check if the item is in the array instead
data = data.filter(cv => ["22", "234"].includes(cv.category));
const data = [{
"name": "Product 2",
"link": "/stock/product2",
"category": "234",
"description": ""
}, {
"name": "Product 1",
"link": "/stock/product1",
"category": "1231",
"description": ""
}, {
"name": "Product 3",
"link": null,
"category": "22",
"description": ""
}]
const filteredData = data.filter(({ category }) => category === "22" || category === "234");
console.log(JSON.stringify(filteredData))
I wouldn't mutate your original object. Also, you can deconstruct category in the filter function.
I have 2 objects, a course list and a user.
The course list is an array with a lot of courses:
[
{
"id": 12345,
"title": "Some title",
"type": [
{
"id": 4700,
"slug": "someType",
"name": "someTypeName"
}
],
"difficulty": [
{
"id": 4704,
"slug": "4",
"name": "hard"
}
],..
},
{...}
The user have also some fields:
{
"difficulty": 4, // the difficulty->slug
"type": "someType" // the type->slug
}
My task:
I want to find the best match between the courses and the user.
In this example the user is looking for type.slug == someType and a difficulty.slug == 4. The slug is always the search term.
My first attempt was:
courseList.filter((course) => {
if (course.type.indexOf(that.userData.type) != -1) {
return course; // dont work
}
});
Edit: I need to display the name and the id properties in the front-end and the "slug" is always the search term.
The filter function takes a function (in your case the arrow function) that returns a boolean so try this instead:
var filterredList = courseList.filter(course => {
return course.type.filter(type => type.slug == that.userData.type).length > 0
&& course.difficulty.filter(difficulty => difficulty.slug == that.userData.difficulty).length > 0
});
You need to compare the slug properties against the user data.
The trick here is to make sure you are filtering the arrays and checking the count.
var courseList = [
{
"id": 12345,
"title": "Some title",
"type": [
{
"id": 4700,
"slug": "someType",
"name": "someTypeName"
}
],
"difficulty": [
{
"id": 4704,
"slug": "4",
"name": "hard"
}
]
},
{
"id": 12346,
"title": "Another title",
"type": [
{
"id": 4701,
"slug": "anotherType",
"name": "anotherTypeName"
}
],
"difficulty": [
{
"id": 4704,
"slug": "4",
"name": "hard"
}
]
}
];
var userData = {
type: 'someType',
difficulty: 4
};
var filteredList = courseList.filter(o =>
o.type.filter(t => t.slug === userData.type).length > 0
&& o.difficulty.filter(d => d.slug === userData.difficulty.toString()).length > 0
);
// Print just the titles of the filtered list
console.log(filteredList.map(o => o.title));