In JavaScript, efficiently remove specific node types from a tree structure using recursion, while also maintaining and distributing qualified child nodes

Currently, I am working on a recursive function that operates on a JSON tree structure {name, type, [children]}, with the goal of removing nodes of a specific type. However, it is essential that the children of the removed node are reattached to the parent if they do not match the type being removed.

I am encountering a challenge in my implementation. Let's consider the scenario where I want to eliminate nodes of type "b" from the following tree:

const exampleData = [{
name: "parent",
type: "a",
children: [{
    name: "childA",
    type: "a",
    children: null
    },{
    name: "childB",
    type: "b",
    children: [{
        name: "grandChildA",
        type: "a",
        children: null
        },{
        name: "grandChildB",
        type: "a",
        children: null
        }]
    },{
    name: "childC",
    type: "a",
    children: null
    }]
}]

The original set of children for the parent node includes [childA, childB, childC]. Following the removal operation, we expect the parent to have children as

[childA, grandChildA, grandChildB, childC]
. However, the current outcome is
[childA, [grandChildA, grandChildB], childC]
.

I acknowledge the need to use the spread operator but I am uncertain about its placement within the recursion logic.

Presented below is the current version of my function (acknowledging that the usage of the spread syntax may be incorrect):

const removeType = (node, type) => {
    // Handling the case where the node should not be removed    
    if (node.type !== type){
        // If the node possesses children, call the function recursively to prune them
        if (node.children && node.children.length > 0){
            node.children = [...node.children.map(child => removeType(child, type))
                                             .filter(child => child !== null)]
            return node
        }
        // If the node has no children, simply return the node itself
        else return node
    }
    // Handling the scenario where the node needs to be removed
    else if (node.type === type){
        // In cases where the node contains children, make a recursive call and then reattach the children
        if (node.children && node.children.length > 0){
            node.children = [...node.children.map(child => removeType(child, type))
                                             .filter(child => child !== null)]
            return node.children
        }
        // Otherwise, return null
        else return null
    }
}

Answer №1

Revised Information

One approach could be to utilize the reduce method for this task. While I don't have access to my computer at this moment to confirm, the structure may resemble the following:

const removeType = (node, type) => {
   if (node === null) {
     return null;
   } else {
    return node.reduce((acc, child) => {
      if(child["type"] === type) {
        const removedChild = removeType(child["children"], type);
        acc = [...acc, ...removedChild];
      } else {
        child.children = removeType(child["children"], type);
        acc.push(child);
      }
      return acc;
    }, []);
  }
}

Latest Update

Streamlined code version:

const removeType = (node, type) => {
    if (!node) return;

    return node.reduce((acc, child) => {
        if(child["type"] === type) {
            const removedChild = removeType(child["children"], type);
            acc = [...acc, ...removedChild];
        } else {
            child.children = removeType(child["children"], type);
            acc.push(child);
        }
        return acc;
    }, []);

}

Answer №2

This particular answer offers a unique perspective compared to the other solutions provided. It follows a recursive structure similar to the one presented by Thankyou, with the added assumption that the input is always an array and all non-nil children nodes are arrays as well.

const filterType = (node, target) =>
  node .flatMap (({type, children, ...rest}) =>
    type === target
      ? children ? filterType (children, target) : []
      : [{...rest, type, children: children && (filterType (children, target))}]
  )

const dataExample = [{name: "parent", type: "a", children: [{name: "childA", type: "a", children: null},{name: "childB", type: "b", children: [{name: "grandChildA", type: "a", children: null},{name: "grandChildB", type: "a", children: null}]}, {name: "childC", type: "a", children: null}]}]

console .log (
 filterType (dataExample, 'b')
)
.as-console-wrapper {min-height: 100% !important; top: 0}

This approach may not necessarily be an enhancement over existing solutions, but it presents an intriguing alternative method.

Answer №3

Here's a program simplification technique involving the use of Array.prototype.flatMap, mathematical induction, and mutual recursion:

  • removeType function accepts an array of nodes and a query type to remove, q
  • removeType1 function accepts a single node and a query type to remove, q
const removeType = (nodes, q) =>
  (nodes || []).flatMap(n => removeType1(n, q))

const removeType1 = (node, q) =>
  q === node.type
    ? removeType(node.children)
    : { ...node, children: removeType(node.children, q) }

const input = 
  [{name:"parent",type:"a",children:[{name:"childA",type:"a",children:null},{name:"childB",type:"b",children:[{name:"grandChildA",type:"a",children:null},{name:"grandChildB",type:"a",children:null}]},{name:"childC",type:"a",children:null}]}]
  
const result =
  removeType(input, "b")

console.log(result)

Output -

[
  {
    "name": "parent",
    "type": "a",
    "children": [
      {
        "name": "childA",
        "type": "a",
        "children": []
      },
      {
        "name": "grandChildA",
        "type": "a",
        "children": []
      },
      {
        "name": "grandChildB",
        "type": "a",
        "children": []
      },
      {
        "name": "childC",
        "type": "a",
        "children": []
      }
    ]
  }
]

Note that the result is a new object and the original input remains unchanged.


In the above problem, mutual recursion works well. But what if you only want one function?

const removeType = (t, q) =>
  Array.isArray(t)                             
    ? t.flatMap(node => removeType(node, q))
: Object(t) === t                              
    ? q === t.type
        ? removeType(t.children, q)
        : { ...t, children: removeType(t.children, q) }
: t                                            

const input = 
  [{name:"parent",type:"a",children:[{name:"childA",type:"a",children:null},{name:"childB",type:"b",children:[{name:"grandChildA",type:"a",children:null},{name:"grandChildB",type:"a",children:null}]},{name:"childC",type:"a",children:null}]}]
  
const result =
  removeType(input, "b")

console.log(result)

The output is almost the same, but notice how this version retains children: null. The original implementation gives the more accurate children: [].

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

There was a syntax error in sign.php on line 15 that read: "Parse error: syntax error, unexpected 'if' (T_IF)"

Encountered an issue: Parse error: syntax error, unexpected '$username' (T_VARIABLE) in /home/rainlikq/public_html/script/signupt.php on line 17 when trying to use the username statement.... Looking for suggestions on how to resolve this. Th ...

Having trouble with the addEventListener function not working on a vanilla JS form?

I'm struggling to get a form working on a simple rails project. The idea is to collect a name and date from the form and display them on the same page using JS. Later, I'll process this information to determine if it's your birthday and cal ...

When attempting to input a value in the middle of the line, the cursor unexpectedly leaps to the end

I have successfully created a code that prevents spaces at the beginning and special characters in an input field. The code is working perfectly, but there is an issue with the cursor moving to the end when trying to type at the beginning or middle of the ...

What is the method for accessing cookies using JSONP?

Hello, I am trying to load a page from index.html using the following code: <script src="https://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.12.2.min.js"></script> <script> function jsonCallback(json){ console.log(json); alert(document.c ...

Fetch the information, insert following, and trigger a slide toggle

I am new to jQuery and I want to create a sliding table. The original table has 3 levels: <ul class="categories"> <li id="category1">1 element</li> //parentid=0 <li id="category2">2 element</li> //parentid=0 <ul> < ...

Using AngularJS to fetch images from RSS feed description

I'm currently learning AngularJS by creating a simple RSS feed. I have successfully made a JSON request and fetched all the data including title, link, description, and images from the RSS feed I parsed. The code snippet for extracting images looks li ...

Interacting with a WCF service using jQuery

Recently, I have been attempting to utilize a JSON file to consume a WCF service. My goal is to extract values from a basic HTML form and then pass those values into an object instantiated in the WCF using JSON. Despite my best efforts, I have not been suc ...

The element is implicitly assigned an 'any' type due to the fact that an expression of type 'any' cannot be used to index types in nodejs and solidity

I am in need of setting networks in my contract using NodeJS and TypeScript. Below is the code I have written: let networkId: any = await global.web3.eth.net.getId(); let tetherData = await Tether.networks[networkId]; Unfortunately, I encountered ...

Tips for efficiently waiting for the outcome in a unified function for multiple callbacks within node.js

Perhaps the question title is not the most appropriate, but let me explain what I am trying to achieve in my code // First Callback Function connection_db.query(get_measure_query,function(err,user_data1){ if(err){ // throw err; ...

Tips for refreshing information in the Angular front-end Grid system?

I am currently working with the Angular UI Grid. The HTML code snippet in my file looks like this. <div ui-grid="gridOptions"></div> In my controller, I have the following JavaScript code. $scope.values = [{ id: 0, name: 'Erik&a ...

Using Sequelize to work with JSON data types

After defining my model like this module.exports = function (sequelize, DataTypes) { const MyModel = sequelize.define('MyModel', { data: { type: DataTypes.JSON, ... }, ... }); return MyModel; }; I am querying it ...

How to access the dynamic route's path in Next.js when using Server Components?

Obtaining the dynamic route path in a Next JS server component poses a challenge. This task is simple when working with client components. If you are working on src/app/[id]/page.tsx "use client"; import { usePathname } from "next/navigatio ...

The function cannot be accessed during the unit test

I have just created a new project in VueJS and incorporated TypeScript into it. Below is my component along with some testing methods: <template> <div></div> </template> <script lang="ts"> import { Component, Vue } from ...

How to Handle the Absence of HTML5 Spellcheck in Specific Web Browsers

While HTML5 spellcheck functionality may vary across different browsers, there are instances where it might not be supported in certain corporate environments. In the event that HTML5 is not supported in a particular browser, it's essential to first c ...

Pressing the Enter key will result in data fetched from JSON being logged multiple times

I am currently implementing a search feature on my React website. When a user enters a keyword in the search input, the keyword is matched in a JSON file. If a match is found, it logs "yes" to the console; otherwise, nothing is logged. Here is an example ...

Display the hover effect for the current card when the mouse is over it

When the mouse hovers over a card, I want only that specific card to show a hover effect. Currently, when I hover over any card, all of them show the hover effect. Is there a way to achieve this in Vue Nuxtjs? Here's what I am trying to accomplish: ...

Sending parameters to a personalized Angular directive

I am currently facing a challenge in creating an Angular directive as I am unable to pass the necessary parameters for displaying it. The directive code looks like this: (function () { "use strict"; angular.module("customDirectives", []) .directive ...

React JS simple validator package not functioning properly with post-property date

I am currently utilizing the simple react validator package for form validation in my react JS project. For those interested, you can find the package at this link: https://www.npmjs.com/package/simple-react-validator However, I have encountered an issue w ...

Error: The current element cannot be clicked. Please try again

I have been attempting to trigger a click event on another button within an onClick event function. An error occurred: Uncaught TypeError: this.clickBack.current.click is not a function The React version being used is 16.8.6 constructor(props) { s ...

Unable to invoke function on HTML element

Recently, I've ventured into writing jQuery-like functions in Vanilla JS. While my selectors seem to be working fine, I encounter an "is not a function" error when trying to call the append function on an HTML element. var $ = function(){ this.se ...