Increase a variable within a recursive javascript function

I have a series of data stored in a variable:

let groups = [
    {
        name: "x",
        selected: false,
        elements: [
            {
                name: "xa",
                selected: false
            },
            {
                name: "xb",
                selected: true
            },
            {
                name: "xc",
                selected: true
            },
            {
                name: "xd",
                selected: false
            }
        ]
    },
    {
        name: "y",
        selected: false,
        elements: [
            {
                name: "ya",
                selected: false
            },
            {
                name: "yb",
                selected: true
            },
            {
                name: "yc",
                selected: true
            },
            {
                name: "yd",
                selected: false
            }
        ]
    }
];

I am looking to determine the number of items with selected = true.
I have created this function for that purpose:

function countSelected(groups, counter = 0) {
    for (let i = 0; i < groups.length; i++) {
        const group = groups[i];
        if (group.selected) {
            counter++;
        }
        if (group.elements && group.elements.length) {
            group.elements.forEach(item => countSelected([item], counter));
        }
    }
    return counter;
}

However, it consistently displays a result of 0.

A functional jsFiddle example

Answer №1

To calculate the total number of selected nodes, a recursive reduce function can be utilized.

const
    countSelectedNodes = (sum, obj) => (obj.nodes || []).reduce(countSelectedNodes, sum + obj.selected);

let categories = [{ name: "a", selected: false, nodes: [{ name: "aa", selected: false }, { name: "ab", selected: true }, { name: "ac", selected: true }, { name: "ad", selected: false }] }, { name: "b", selected: false, nodes: [{ name: "ba", selected: false }, { name: "bb", selected: true }, { name: "bc", selected: true }, { name: "bd", selected: false }] }],
    count = categories.reduce(countSelectedNodes, 0);

console.log(count);

Answer №2

One reason is that when Number (and other primitives) arguments are passed to a function, they are duplicated, creating a completely new variable with the argument's value.

To work around this issue, you can either use the return value of your function and add it up, or use Objects as arguments since they are passed by reference, allowing you to maintain the same object:

let categories = [{name: "a",selected: false,nodes: [{name: "aa",selected: false},{name: "ab",selected: true},{name: "ac",selected: true},{name: "ad",selected: false}]},{name: "b",selected: false,nodes: [{name: "ba",selected: false},{name: "bb",selected: true},{name: "bc",selected: true},{name: "bd",selected: false}]}]

function getSelectedWithObject(categories, counter = {val: 0}) {
    for (let index = 0; index < categories.length; index++) {
        const category = categories[index]
        if (category.selected) {
            counter.val++
        }
        if (category.nodes && category.nodes.length) {
            category.nodes.forEach(cat => getSelectedWithObject([cat], counter))
        }
    }
    return counter.val
}

function getSelectedWithReturnValue(categories) {
    let counter = 0
    for (let index = 0; index < categories.length; index++) {
        const category = categories[index]
        if (category.selected) {
            counter++
        }
        if (category.nodes && category.nodes.length) {
            category.nodes.forEach(cat => counter += getSelectedWithReturnValue([cat]))
        }
    }
    return counter
}

console.log(getSelectedWithObject(categories))
console.log(getSelectedWithReturnValue(categories))

Answer №3

counter is defined as an integer and passed by value in this scenario. Set the counter variable to store the desired result from the function like so:

function getSelected(categories) {
    var counter = 0;
    for (let index = 0; index < categories.length; index++) {
        const category = categories[index];
        if (category.selected) {
           counter++;
        }
        if (category.nodes && category.nodes.length) {
           category.nodes.forEach(cat => counter += getSelected([cat]));
        }
    }
    return counter;
}

console.log(getSelected(categories));

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

Tallying outcomes using JavaScript

I encountered a particular challenge: I have designed a table for user interaction, with results displayed at the end of each row. Just out of curiosity, I would like to count how many results are present in the table without performing any calculations. I ...

Function compilation did not succeed in the module

I've put together a MERN (MongoDB, ExpressJS, React, Node) project using express-generator (express myNewApp). Within a react component, I have implemented this ES6 code snippet: onChange = (event, { newValue }) => { // Line 53 this.setSt ...

Keyboard-enabled jQuery function to smoothly scroll the page to a specified heading

I have encountered an issue with a piece of code that generates a list of all the h2 elements on a page with clickable links, but unfortunately, it lacks keyboard accessibility. What I am aiming for is to have the ability to select the this element mentio ...

Change the class name using jQuery when scrolling

Currently utilizing bootstrap as my primary css framework. My goal is to dynamically toggle a class on the navbar once the user scrolls past the prominent header image located at the top of the website. UPDATE: Admitting I made an error and had a momenta ...

add an svg string element to a pre-existing svg element

I already have an <svg></svg> element in my code and now I want to add another svg element to it as a string. For instance: var new_svg = '<g><text x="100" y="100">Hello</text></g>'; d3.select('svg' ...

get a duplicate of an object

Is this the proper method for creating a duplicate of an object? class ObjectWrapper { private _obj; /*** * Copy object passed as argument to this._obj */ constructor (_obj: Object) { this._obj = _obj; } /** Return copy of this._ ...

Ajax request and the Ghostery extension in Firefox

I've implemented the following code to detect ad blockers like Ghostery: <script> var request = new XMLHttpRequest(); request.onreadystatechange = function() { if(request.readyState === 4 && request.status === 200 ) { ...

Struggling with a component that won't load in JSX?

Having some difficulty with React not rendering data associated with a component's props: import React from 'react'; import {ItemListing} from './ItemListing.js'; export var SearchResults = React.createClass({ render: functi ...

How to show multiline error messages in Materials-UI TextField

Currently, I am attempting to insert an error message into a textfield (utilizing materials UI) and I would like the error text to appear on multiple lines. Within my render method, I have the following: <TextField floatingLabelText={'Input Fi ...

The disabled feature doesn't seem to be functioning properly with the <i> tag when using Font Awesome icons

I've designed a plunk where I'm trying to validate a form with a button featuring a font-awesome icon. The text field is mandatory and I'd like to disable the icon if no data has been entered. However, it seems that ng-disabled doesn't ...

What is going on with the current status of these two arrays?

int[] a = new int [] {1,2,3}; int[] b = new int [] {4,5,6,7}; System.out.println(a[0] + ", " + b[0]); a = b; System.out.println(a[0] + ", " + b[0]); a[0] = 42; System.out.println(a[0] + ", " + b[0]); Output: 1, 4 4, 4 42, 42 I am puzzled ...

Dependencies in Angular

After diving into Angular recently, I've been grasping the concepts well. However, one thing that still puzzles me is dependency injection. I'm unsure whether it's necessary to declare all components of my application (services, controllers ...

What is the best way to retrieve the values from the labels for two separate buttons?

I designed a pair of buttons with a single label below. Both buttons acted as standalone entities. <label for="buttons" class ="val">0</label> <input class="btn btn-primary button1" type="button" ...

Top location for securely storing information in Angular 8

I have developed a web application using Angular 8. My goal is to secure routes and pages with dynamic access levels. For instance, I want to verify if a user has access to a specific route, and if not, redirect them to the login page. To do this, I cur ...

What is the best way to invoke React component code from renderer.js?

Hello everyone, I am diving into the world of React/JS and Electron and have a goal to develop a desktop application using these amazing technologies. Currently, my biggest challenge is figuring out how to call react component code from renderer.js. Let m ...

What is the best way to establish a connection between the same port on Expressjs and Socket.io

I am currently using Express.js and Socket.io to develop a chat application. Initially, I created my project with Express-Generator and began by running the node ./bin/www script. However, I decided to remove the ./bin/www file and instead combined it wit ...

What is the best way to save the data from a char* pointer?

The code provided below demonstrates how to convert an int/char value to binary: char* int2bin(int value, char* buffer, int bufferSize) { char *nextChar = buffer + bufferSize-1; // location to write the least significant bit *nextChar ...

How can I show the unique phrase "Today" on a specific date (date=today) with the Angular-UI-Bootstrap Datepicker?

I'm currently integrating the Angular-UI datepicker directive into my project and I'm facing an issue. I want to have the datepicker display 'Today' instead of the actual date like 2015/04/27. Is there a way to achieve this without hav ...

Invoking a function within the directive's controller

How can I access and call the method defined within the directive controller externally? <div ng-controller="MyCtrl"> <map></map> <button ng-click="updateMap()">call updateMap()</button> </div> app.directive(&a ...

JSON object name

Here are the specific file locations for loading each of the CSS and JS files. <link href="css/default.css" rel="stylesheet" /> <script src="js/main.js"></script> In XML, the filename is input as shown below ...