Can someone provide guidance on effectively implementing this JavaScript (TypeScript) Tree Recursion function?

I'm currently grappling with coding a recursive function, specifically one that involves "Tree Recursion". I could really use some guidance to steer me in the right direction.

To better explain my dilemma, let's consider a basic example showcasing the data and my objective. Please note that the actual data is significantly more complex...

Essentially, I begin with an array containing a single array of objects as shown below, where the prop2 in any object can either be a valid string or an empty string...

[
    [
        { prop1: "abc", prop2: "" },
        { prop1: "def", prop2: "one" },
        { prop1: "ghi", prop2: "" }
    ]
]

The task at hand for my algorithm is to examine the above array, iterate over the objects, and once it encounters an object with an empty string in prop2, it must duplicate the array three times. Subsequently, it should replace the empty string in only that specific object with three different values (one/two/three) like so...

[
    [
        { prop1: "abc", prop2: "one" },
        { prop1: "def", prop2: "one" },
        { prop1: "ghi", prop2: "" }
    ],
    [
        { prop1: "abc", prop2: "two" },
        { prop1: "def", prop2: "one" },
        { prop1: "ghi", prop2: "" }
    ],
    [
        { prop1: "abc", prop2: "three" },
        { prop1: "def", prop2: "one" },
        { prop1: "ghi", prop2: "" }
    ]
]

Following this modification, the algorithm restarts afresh with the new array consisting of three arrays.

During the subsequent iteration, each of the three arrays will be replicated thrice and the empty string will undergo a similar replacement process.

In this simple instance, the end outcome would be an array comprising nine arrays.

If the original array contained more objects with empty prop2 values, additional iterations would be necessary.

Fundamentally, I am transforming an array of objects with certain props being empty strings by broadly "expanding" those particular prop values to encompass every possible permutation of "one"/"two"/"three".

Although recursion seems ideally suited for this problem, I am encountering challenges when trying to formulate the code.

It appears that the "base case" would entail having an array of objects wherein none possess properties with empty strings – returning said array in such scenarios.

However, I am uncertain about how the other case should be structured beyond invoking the same function thrice with the newly generated variants. The desired output for this alternate scenario also remains unclear to me.

Unfortunately, I have not been able to locate relevant examples resembling the task at hand online.

EDIT: Upon reviewing the responses suggesting recursive solutions, while they indeed function accurately, it is now apparent that arriving at a recursive solution has proven far from straightforward. Surprisingly, the non-recursive approach emerges as the most effective solution.

Answer №1

Here is a unique solution that can help meet your desired outcome:

const myTree = [
    [
        { prop1: "abc", prop2: "" },
        { prop1: "def", prop2: "one" },
        { prop1: "ghi", prop2: "" }
    ]
];

let nodesWithEmptyStrings = myTree.filter(n=> !!n.find(l=> l.prop2===""));
while(nodesWithEmptyStrings.length > 0) {
  nodesWithEmptyStrings.forEach(n => {
    const firstEmptyStringLeaveIndex = n.findIndex(l=> l.prop2==="");
    n[firstEmptyStringLeaveIndex].prop2 = "one";
    const copy1 = JSON.parse(JSON.stringify(n));
    copy1[firstEmptyStringLeaveIndex].prop2 = "two";
    myTree.push(copy1);
    
    const copy2 = JSON.parse(JSON.stringify(n));
    copy2[firstEmptyStringLeaveIndex].prop2 = "three";
    myTree.push(copy2);
  });
  nodesWithEmptyStrings = myTree.filter(n=> !!n.find(l=> l.prop2===""));
}

document.getElementById('result').innerText = JSON.stringify(myTree, null, 2);
<pre id="result"></pre>

Answer №2

Indeed, recursion is the key to achieving this task. The fundamental concept is to update the array and then inspect if further modifications are necessary; if so, recursively call the function with the updated array.

Below is a practical example:

const fillers = ['one', 'two', 'three'];
const propToCheck = 'prop2';

function modifyArray(arr) {
  const modified = arr.reduce((a, c) => {
    const found = c.find(v => !v[propToCheck]);
    if (found) {
      const tmp = c.filter(v => v !== found);
      return [...a, ...fillers.map(filler => [...tmp, { ...found, [propToCheck]: filler }])];
    }
    return [...a, c];
  }, []);
  const notDone = modified.some(v => v.some(o => !o[propToCheck]))
  if (notDone) {
    return modifyArray(modified);
  }
  return modified;
}

const result = modifyArray([
    [
        { prop1: "abc", prop2: "" },
        { prop1: "def", prop2: "one" },
        { prop1: "ghi", prop2: "" }
    ]
]);

console.log(result);

Answer №3

When faced with a problem that may not be naturally approached using recursion, an alternative solution involving a general function can prove effective. This particular function accepts substitutions and a key for identifying empty strings as arguments, leveraging ES6+ syntax and destructuring.

const substitute = (data, index, keyToCheck, substitutes) => {
  const indexOfObjectWithEmptyKeyToCheck = data[index].findIndex(obj => obj[keyToCheck] === "")
    if(indexOfObjectWithEmptyKeyToCheck === -1) {
        if(index === data.length - 1)
            return data
        else
            return substitute(data, index + 1, keyToCheck, substitutes)
    }
    else {
        return substitute(
            [
                ...data.slice(0, index),
                ...(substitutes.map(
                    substitute => [
                        ...data[index].slice(0, indexOfObjectWithEmptyKeyToCheck),
                        { ...data[index][indexOfObjectWithEmptyKeyToCheck], [keyToCheck]: substitute },
                        ...data[index].slice(indexOfObjectWithEmptyKeyToCheck + 1)
                    ]
                )),
                ...data.slice(index + 1)
            ],
            index,
            keyToCheck,
            substitutes
        )
    }
}


const SUBSTITUTES = ["one", "two", "three"];

const result = substitute(
    [
        [
            { prop1: "abc", prop2: "" },
            { prop1: "def", prop2: "one" },
            { prop1: "ghi", prop2: "" }
        ]
    ],
    0, 
    "prop2", 
    SUBSTITUTES
)
console.log(result)
console.log("Size of result: " + result.length)

The approach involves iterating through the array, adjusting the index only when an object with the specified key as an empty string is found. When necessary, replacements are made, and the function recurses on the same index. The termination condition occurs when the key to check is not an empty string and the current index is the last one in the input array.

I'll leave the TypeScript implementation up to you as it's more about typing the input data rather than solving the primary issue at hand.

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

Utilizing ReactStrap: a guide to retrieving the id of the chosen dropDownItem

In my code, I have a dropdownList component with various DropdownItems: <Dropdown isOpen={this.state.dropdownOpen[3]} toggle={() => { this.toggle(3); }} > <DropdownToggle className="my-dropdown" car ...

I keep seeing this strange [object HTMLSpanElement] appearing on my HTML page

Thanks for the help, the issue has been resolved and I appreciate your valuable time! ...

The Algolia Hit Component is having difficulty functioning properly within a grid layout

I am in the process of converting my next API to an Algolia search. The Hit component is a single component that renders for each record. However, I am facing an issue with implementing a grid layout. Below is the code snippet from before (which was workin ...

Ensuring input validity and blocking changes if not compliant with AngularJS patterns

I am looking to create an input field that only accepts the values 1, 2, or 3. To achieve this, I am working on a directive that will prevent any changes to the model if it doesn't match these specific values. For example, if the value is 1 and I tr ...

Discovering the specific URL of a PHP file while transmitting an Ajax post request in a Wordpress environment

I'm currently working on a WordPress plugin and running into some issues with the ajax functionality. The main function of my plugin is to display a form, validate user input, and then save that data in a database. Here is the snippet of code for my ...

Storing data from a collection of interface objects in a string array

Take a look at the following code snippet: import React, {FC} from 'react'; import {useFetchErrors} from "../Api/Api"; import {useLocation} from "react-router-dom"; interface ExecutionTableProps { project_id: number } const ...

Activating gzip compression using fetch.js

I am utilizing fetch.js (https://github.com/github/fetch) to transmit a rather substantial JSON object to the backend. The size of the JSON is significant due to it containing an SVG image string. My question is whether fetch.js applies gzip compression a ...

Setting up a CentOs Linux environment to host and run a node.js server

Just installed node.js on my new VPS server. I have written a basic script called "server.js" and it is currently running on port 8080. The location of the script is in the "var/www/html/" directory. However, whenever I try to access my domain, it only dis ...

Matching multiline input with RegExp and grouping matches from consecutive lines

Imagine having a text file with the following content: AAAA k1="123" k2="456" several lines of other stuff AAAA k1="789" k2="101" AAAA k1="121" k2="141" The objective is to extract the values of k1 and k2 while keeping them grouped together. For instance ...

Next.js application shows 404 errors when trying to access assets within the public directory

I've been struggling to display the favicon for my site (which uses next.js). Despite going through numerous Stack Overflow posts and tutorials, I'm feeling increasingly frustrated. The structure of my project, particularly the public directory, ...

Error encountered when attempting to convert a JSON object to a String using JSON.stringify(), due to a cyclic object value

I have a JSON object with the following structure: obj { name: "abc" , entriesList : "list of entry obj" , propertiesList : "list of properties obj" }; In this structure, an entry is also another object entry { info : "data obj" , ...

Angular.js: how to filter an ng-repeat by a missing key/value pair

How can I filter the ng-repeat to only display rows where all key/value pairs are present? HTML <div ng-app="myApp" ng-controller="oneController"> <table class="table table-bordered table-hover"> <thead> <tr> <th> ...

The C++ Induction Algorithm displays sluggish performance when contrasted with Dynamical Programming techniques

I am currently facing a mathematical control problem that I am tackling using Backward Induction. The core of the issue lies in the following scenario: Considering K being less than n. And defining final conditions. The burning question is, what exactl ...

Utilizing query parameters in JavaScript

When querying data from the database using passed parameters, I encountered an issue. For example: http://localhost:3030/people?$skip=0&$limit=25&$sort[name]=0&description[$name]=rajiv I wanted to add an extra parameter without including it in ...

Angular - enabling scroll position restoration for a singular route

Is there a way to turn off scroll restoration on a specific page? Let's say I have the following routes in my app-routing.module.ts file... const appRoutes: Routes = [{ path: 'home', component: myComponent}, { path: 'about', compon ...

Setting up React Router in a nested directory with a flexible route structure

As a newcomer to react router, I am seeking guidance on setting it up in a specific scenario. Imagine we have a PHP application running on 'http://www.example.com'. Within this setup, there is a react application located at 'http://www.examp ...

Is There a Way to Abandon a route and Exit in Express during a single request?

In order to ensure proper access control for the application I was developing, I structured my routing system to cascade down based on user permissions. While this approach made sense from a logical standpoint, I encountered difficulties in implementing it ...

Implementing the jquery mobile data-native-menu feature in a select element dynamically generated from jeditable

Greetings! I have encountered an issue where the data-native-menu="false" instruction works correctly when directly placed in a select element, but doesn't work when added to a select generated by JavaScript (using the Jeditable plugin). You can view ...

Ways to ensure all images have been successfully uploaded to Firebase before executing a function

Here's a function I've created that takes image URIs from the state, uploads them to Firebase Storage, and then updates the state with the download URLs: uploadImages = async (userID) => { // Loop through each image for (var index = 0 ...

What measures can be taken to keep the rightmost element from moving when hovered over?

While I am generally happy with the layout, there seems to be a slight jump to the left when hovering over the right-most image (you need to click "show images" to view them). Strangely, this issue does not occur with the last row of images. Any suggestion ...