Removing all items with a specific ID and its related items in Javascript: How to achieve this recursively?

I am currently facing some challenges in figuring out the most effective approach for this scenario.

For example, consider the data structure below:

const arr = [{
  parentId: 1,
  children: [
    { childId: 11 },
    { childId: 21 },
    { childId: 31 },
  ]
}, {
  parentId: 31,
  children: [
    { childId: 111 },
    { childId: 211 },
    { childId: 311 },
  ]
}, {
  parentId: 7,
  children: [
    { childId: 711 },
    { childId: 721 },
    { childId: 731 },
  ]
}, {
  parentId: 311,
  children: [
    { childId: 3111 },
    { childId: 3211 },
    { childId: 3311 },
  ]
}]

If I need to delete parentId = 1, I would also like to remove all its associated children. Additionally, if any of these removed children have an associated parentId, those children and their subsequent generations should also be removed. For instance, parentIds 11, 21, 31 should be removed. Since 31 is a parentId, we must remove its childIds - 111, 211, and 311. Furthermore, as 311 is also a parentId, its childIds should be removed as well. There are no further childIds linked to parentId 311, so we can stop at that point.

If anyone has suggestions on the best approach to tackle this issue, your guidance would be greatly appreciated. I was considering utilizing recursion, but the examples I've come across primarily deal with nested arrays rather than separate arrays that require repeating the process.

The resulting data structure after processing would be:

[{
  parentId: 7,
  children: [
    { childId: 711 },
    { childId: 721 },
    { childId: 731 },
  ]
}]

Answer №1

Recursion is not necessary in this scenario, thanks to the organized and linear data structure. You can effectively filter using a closure with a Set where all child IDs are gathered.

const
    remove = (data, id) => data.filter((s => o => {
        if (!s.has(o.parentId)) return true;
        o.children.forEach(({ childId }) => s.add(childId));
    })(new Set([id]))),
    data = [{ parentId: 1, children: [{ childId: 11 }, { childId: 21 }, { childId: 31 }] }, { parentId: 31, children: [{ childId: 111 }, { childId: 211 }, { childId: 311 }] }, { parentId: 7, children: [{ childId: 711 }, { childId: 721 }, { childId: 731 }] }, { parentId: 311, children: [{  childId: 3111 }, { childId: 3211 }, { childId: 3311 }] }],
    result = remove(data, 1);

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Answer №2

One approach is to create a queue of IDs to be deleted, starting with the ID at the top of the queue and adding any dependent IDs to the end. To simplify things, let's convert your array into a map that pairs IDs with corresponding objects:

const arr=[{parentId:1,children:[{childId:11},{childId:21},{childId:31}]},{parentId:31,children:[{childId:111},{childId:211},{childId:311}]},{parentId:7,children:[{childId:711},{childId:721},{childId:731}]},{parentId:311,children:[{childId:3111},{childId:3211},{childId:3311}]}]

//

let m = new Map(arr.map(o => [o.parentId, o]))
let remove = [1]

while (remove.length > 0) {
    let id = remove.shift()
    let obj = m.get(id)
    if (obj) {
        console.log('debug: deleting', id)
        m.delete(id)

        remove.push(...obj.children.map(c => c.childId))
    }
}

let result = [...m.values()]
console.log(result)

Answer №3

Here's a potential solution utilizing recursion and the Lodash.js library:

import * as _ from 'lodash'

const initialArr = [
  { parentId: 1, children: [{ childId: 11 }, { childId: 21 }, { childId: 31 }] }, 
  { parentId: 31, children: [{ childId: 111 }, { childId: 211 }, { childId: 311 }] }, 
  { parentId: 7, children: [{ childId: 711 }, { childId: 721 }, { childId: 731 }] }, 
  { parentId: 311, children: [{ childId: 3111 }, { childId: 3211 }, { childId: 3311 }] }
]

const removeChildren = (id, arr) => _.flow(
  _.find(_.matchesProperty("parentId", id)),           // find parent
  _.getOr([], "children"),                             // handle zero case  
  _.map("childId"),                                    // find the children    
  _.reduceRight(removeChildren, arr),                 // recurse the children
  _.reject(_.matchesProperty("parentId", id))         // then remove the parent
)(arr)

console.log( JSON.stringify( removeChildren(1, initialArr) ) )
// [{ parentId: 7, children: [{ childId: 711 },{ childId: 721 },{ childId: 731 }] }]

Implementing a functional-programming approach like this often results in code that is easier to understand over time. It also promotes the use of immutable data structures, reducing the risk of unintended side effects from modifications. However, this method may not always be the most efficient at runtime.

Answer №4

One way to tackle this is by utilizing recursion.

const data=[{parentId:1,children:[{childId:11},{childId:21},{childId:31}]},{parentId:31,children:[{childId:111},{childId:211},{childId:311}]},{parentId:7,children:[{childId:711},{childId:721},{childId:731}]},{parentId:311,children:[{childId:3111},{childId:3211},{childId:3311}]}];

function myRecursiveFunction(inputData, key) {
  for (let [i, item] of inputData.entries()) {
    if (item.parentId && item.parentId === key) {
      const childNode = item.children;
      if (childNode.length) {
        for (let child of childNode) {
          myRecursiveFunction(inputData, child.childId);
        }
      }
      inputData.splice(i, 1);
    }
  }
  return inputData;
}

const modifiedData = myRecursiveFunction(data, 1);
document.getElementById('output').innerHTML = JSON.stringify(modifiedData, null, 2);
<pre id="output"></pre>

Answer №5

This method employs recursion alongside lodash:

const array = [{
  parent: 1,
  children: [
    { child: 11 },
    { child: 21 },
    { child: 31 },
  ]
}, {
  parent: 31,
  children: [
    { child: 111 },
    { child: 211 },
    { child: 311 },
  ]
}, {
  parent: 7,
  children: [
    { child: 711 },
    { child: 721 },
    { child: 731 },
  ]
}, {
  parent: 311,
  children: [
    { child: 3111 },
    { child: 3211 },
    { child: 3311 },
  ]
}]

const removeByParentId = (array, parent) => {
  const parentObj = _.find(array, { parent });
  if (!parentObj) return array;
  
  let updatedArray = _.reject(array, { parent });
  parentObj.children.forEach(child => {
    updatedArray = removeByParentId(updatedArray, child.child);
  });

  return updatedArray;
};

const resultArray = removeByParentId(array, 1);

console.log(resultArray);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js"></script>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Running Windows commands from Node.js on WSL2 Ubuntu and handling escape sequences

When running the following command in the CMD shell on Windows, it executes successfully: CMD /S /C " "..\..\Program Files\Google\Chrome\Application\chrome.exe" " However, attempting to run the same comman ...

Removing all text inside an input field with Vue

I am trying to create a password input field using type 'text' instead of 'password.' <input type="text" v-model="form.password" @input="test" /> <input type="hidden" v-model="form.hiddenPassword" /> As part of my approach ...

Transforming values in a 2-dimensional array from strings to numerical data

Data From External Source Hello there, I've been utilizing the read_csv function in Python to pull data from a specified URL. The dataset I'm working with contains a column filled with character/string values, while the remaining columns are co ...

Convert form data into a JSON object using Vue.js

I'm attempting to generate a JSON object from the submitted form data. Fortunately, I've accomplished this using a variable. However, is there an alternative approach for creating a JSON object? Form <form @submit.prevent="submit"& ...

Can README docs be prioritized to appear before all other stories in Storybook navigation?

Organizing my Storybook stories is important to me. I like to nest all my stories under a “Docs” header, with each component having a README mdx file followed by its stories. My preferred order is to always have the README appear first in the navigatio ...

AJAX using PHP returns an object containing null values

As a beginner in ajax programming, I encountered a small issue. I created a function in jQuery that makes an ajax call to a PHP file, which then retrieves information about a player from the database. However, when the PHP file responds to the ajax functio ...

The image fails to load when attempting to retrieve it from a local JSON file

I successfully managed to fetch data dynamically from a local JSON file created in RN. However, when I tried to add images for each profile to be displayed along with the dynamic profile info, the app encountered an error stating that "The component cannot ...

Having trouble executing the yarn command for clasp login

Issue with running yarn clasp login I am not very proficient in English, so please bear with me. 8> yarn clasp login yarn run v1.22.22 $ C:\Users\myname\Desktop\個人開発プロジェクト\clasp-240418\node_modu ...

What is the technique used by express.js to handle ReferenceError?

// Here is a sample code snippet app.get("/test", (req, res) => { return res.status(200).send(SOME_UNDEFINED_VAR); }); If a ReferenceError occurs, express.js will automatically send a 500 error response. express.js logs the ReferenceError to std ...

Enhanced Fancybox Version 2 adjusts iframe width to fit content

I have been attempting to adjust the width of the iframe in fancybox v2 to fit my content properly. However, every time I try, the content appears too large within the iframe and requires horizontal scrolling to view it all. My goal is to see the entire wi ...

Checking to see if all the users mentioned in the message have been assigned a role

Hello everyone, I'm new to asking questions here so please bear with me. I am trying to retrieve all the users mentioned in a message and check if any of them have a specific role, such as the staff role. Thank you for your help! Edit: Here is the ...

Tips for efficiently finding and comparing text within the results generated by a for loop in Angular/Javascript

In my XML data, I have extracted all the tag names using a for loop and some other logic. Now, I am looking to find the word 'author' from the output values that are displayed in the console during the loop. If any of the output values match &apo ...

I prefer the user not engage with the background when the jQuery dialog is displayed

When a user clicks on an external link, the jQuery UI dialog box will appear with "ok" and "cancel" buttons. Upon clicking "ok," the user should be directed to the external site. Although everything is functioning correctly, there is an issue with the mod ...

I can't see the presence of spin.js on my website

I am completely new to Javascript and I'm trying to implement a loading spinner on my website. Whenever users tap the screen, it takes some time to navigate to the desired URL, so we need a spinner to prevent them from tapping repeatedly. I decided t ...

Transform a 2-dimensional array of numbers into an array of objects labeled with a "row" index

Today my brain seems to be in full-on "fart" mode. Struggling to find a clean solution for this task without resorting to an ugly loop or recursion. I have a 2D array of numbers that looks like this: const layoutMatrix = [ 1, [1], [0.5, 0.5], [0.2 ...

Do we need to employ strict mode when utilizing specific ES6 functions in Node.js?

There has been a debate circulating at my workplace regarding whether or not it is necessary to include 'use strict' when using ES6 in Node.js without Babel. Some argue that certain ES6 methods may not function correctly without it, but I haven&a ...

Making a HTTP Get request for a single item in Ionic 2

I have successfully implemented an API to retrieve data and display it on the page. It works perfectly for a json response containing more than one object, as it follows a "matches" hierarchy. However, I am facing an issue when trying to print out data for ...

When trying to import the "firebase/app" module, an error occurred stating that the default condition should be the last one to be considered

Greetings! I am diving into the world of webpack and firebase. Every time I use: import { initializeApp } from "firebase/app"; I encounter this error message: https://i.sstatic.net/tvuxZ.png Here is my file structure: https://i.sstatic.net/406o ...

Tips for repeatedly clicking a button over 50 times using Protractor

Is it possible to click the same button more than 50 times using a loop statement in Protractor? And will Protractor allow this action? Below is my locator : var nudge= element(by.xpath("//a[@class='isd-flat-icons fi-down']")); nudge.click(); ...

Implementing jQuery/JavaScript to efficiently iterate through JSON data

I have implemented a form that allows users to select an item from a multi-select dropdown (A). If the desired item is not listed, users can manually add it using another text input box (B). Once added, an AJAX call saves the item to the database. After su ...