toHo
toHo

Reputation: 438

Remove item from nested array object in Javascript by key value

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 :)

Upvotes: 0

Views: 1518

Answers (3)

vs_lala
vs_lala

Reputation: 735

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.

Upvotes: 3

Nick Parsons
Nick Parsons

Reputation: 50684

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 */

Upvotes: 3

huytc
huytc

Reputation: 1141

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
        }
    })
})

Upvotes: 1

Related Questions