The system encountered difficulty handling a recursive structure

I am facing a challenge with a recursive JSON structure that needs to be stored as a series of maps with keys. The structure comprises flows and subflows that have references to each other. Here are the type declarations, noting that the issue lies in the faulty algorithm:

    type Flow = {
       flowId: number,
       subFlows?: Array<SubFlow>
    } 

    type SubFlow = {
      subFlowId: number,
      flow?: Flow,
    }

    type FlowGroup = {
        flows: Map<number, Flow>,
        subFlows: Map<number, Flow>
    }

The end goal is to store the flow within a flowgroup structure using flowIds as keys. Below is an example of the JSON data:

             {
               "flowId": 90112,
               "subFlows": [
                {
                "subFlowId": 52820,
                "flow": {
                   "flowId": 80032,
                   "subFlows": [
                    { 
                      "subFlowId": 76422
                    }, 
                    {
                       "subFlowId": 12654
                    }
                  ]
                    }
                   },
                   {
                      "subFlowId": 12422
                   }
               ]
            }

However, my attempt at populating this results in an empty flowgroup. Here is the code snippet I used:

   const mainFlow:Flow = { 
     flowId: 90112,
     subFlows: _subFlows
     }

   
   function processFlow(flow: Flow) {
     if (flow && flow.subFlows === undefined) {
        flowGroup.flows.set(flow.flowId, flow);
     } else if (flow && Array.isArray(flow.subFlows)) {
        for (const subFlow of flow.subFlows) {
            flowGroup.subFlows.set(subFlow.subFlowId, subFlow);
            if (subFlow.flow) {
                processFlow(subFlow.flow);
            }
        }
    }
}

processFlow(mainFlow);
console.log(`FlowGroup is ${JSON.stringify(flowGroup)}`);

Here is the data used to create the test scenario:

const subFlow1:SubFlow ={
 subFlowId: 52820
 }

const subFlow2:SubFlow ={
 subFlowId: 12422
}

 const flow3:SubFlow = {
  subFlowId: 76422
 }

 const flow4: SubFlow = {
  subFlowId: 12654
 }

 const flow5: Flow = {
  flowId: 80032,
   subFlows: [flow3, flow4]
 }

subFlow1.flow = flow5;  

 const flowGroup: FlowGroup = {
      flows: new Map<number, Flow>(),
      subFlows: new Map<number, Flow>()
    }

 const _subFlows = [subFlow1, subFlow2];

 const mainFlow:Flow ={
  flowId: 90112,
  subFlows: _subFlows
 }

Manually printing the values of a map set:

    const m = new Map<string, Flow>();
    m.set(flow5.flowId, flow5);
    
    for(const [k, v] of m.entries()){
      console.log(`Map key = ${k} and value = 
             ${JSON.stringify(v)}`);
      }

These are the TypeScript compiler settings used:

   "target": "esNext",
   "module": "es2022",
   "moduleResolution": "node",

Highlighted below is a piece of successful implementation in the application:

productHandler<Product, ProductKey
    extends keyof Product>(product: Product, productKey: ProductKey) {
    const map = new Map<ProductKey, Array<string>>();
    const productData = product[productKey] as unknown as Array<string>;
    map.set(productKey, productData)
    return map;
}

Answer №1

It seems like I understand the approach you are trying to take here. To make it more comprehensible, providing an example that outlines both inputs and outputs would be beneficial.

When dealing with recursive processing of a structure comprising N node types, the most straightforward method is employing a set of N mutually recursive functions. An illustration of this concept could look something like the following:

type Flow = {
  flowId: number,
  subFlows?: Array<SubFlow>
} 

type SubFlow = {
  subFlowId: number,
  flow?: Flow,
}

type FlowGroup = {
  flows: Map<number,Flow>,
  subFlows: Map<number,Flow>
}
    
const subFlow1:SubFlow = {
  subFlowId: 52820
}

const subFlow2:SubFlow = {
  subFlowId: 12422
}

const flow3:SubFlow = {
  subFlowId: 76422
}

const flow4: SubFlow = {
  subFlowId: 12654
}

const flow5: Flow = {
  flowId: 80032,
  subFlows: [flow3, flow4]
}

subFlow1.flow = flow5;  

const _subFlows = [subFlow1, subFlow2];

const mainFlow: Flow = {
  flowId: 90112,
  subFlows: _subFlows
}

const flowGroup: FlowGroup = {
  flows: new Map<number, Flow>(),
  subFlows: new Map<number, Flow>()
}

function accumulateFlow(flow: Flow) {
  if (flow == null) {
    return;
  }
  flowGroup.flows.set(flow.flowId, flow);
  for (let subFlow of flow.subFlows) {
    accumulateSubflow(subFlow);
  }
}

function accumulateSubflow(subFlow: SubFlow) {
  flowGroup.subFlows.set(subFlow.subFlowId, subFlow);
  accumulateFlow(subFlow.flow);
}

accumulateFlow(mainFlow)
console.log('Flows ' + JSON.stringify(Array.from(flowGroup.flows.keys())))
console.log('SubFlows ' + JSON.stringify(Array.from(flowGroup.subFlows.keys())))

This results in:

"Flows [90112,80032]"
"SubFlows [52820,76422,12654,12422]"

The current implementation may be correct, but there could still be room for improvement or clarification.

In situations where accumulatesubflow is uncomplicated and called only once, one might consider consolidating it into the parent function. However, doing so limits flexibility for future scenarios where the root structure could potentially be a sub-flow instead of a flow.

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

What benefits does Observable provide compared to a standard Array?

In my experience with Angular, I have utilized Observables in the state layer to manage and distribute app data across different components. I believed that by using observables, the data would automatically update in the template whenever it changed, elim ...

Angular: Failure in receiving EventEmitter's event with .subscribe method

My current challenge involves handling an event coming from NgLoopDirective within the method EV of NgDNDirective. I am attempting to achieve this by passing the EventEmitter object by reference and then calling .subscribe() as shown in the code snippet be ...

Avoiding multiple HTTP requests on various subscribers in RXJS/Angular

I am currently utilizing the "mainData" service, which is composed of 3 key parts: currentPage is utilized by the paginator component for page navigation and can be updated dynamically. folders holds all folders within the current directory. This observa ...

Exploring nested promises in TypeScript and Angular 2

I have a method called fallbackToLocalDBfileOrLocalStorageDB, which returns a promise and calls another method named getDBfileXHR, also returning a promise. In the code snippet provided, I am unsure whether I need to use 'resolve()' explicitly o ...

What is the best way to export a ReactTS component along with its children?

After importing the component, I proceed to declare a new component which will be a child for the invoked one. import { someComponent } from './someComponent'; This is how I export it: const anotherComponent = () => {...}; export { someCompon ...

Error Encountered When Searching for Modules in a Yeoman-Generated Express TypeScript Project

After generating an express typescript project using yeoman, I encountered some errors whenever I tried running the application. The errors stated that it could not find modules such as "morgan", "body-parser", and "cookie-parser". Even though these module ...

Base URL for making Http Requests in an Angular application

I am currently working on an angular application that is hosted on a test server running IIS with a .net core backend. The application is set up on a virtual directory, for example www.myTestApp/crm (the actual domain name being fictional). During the buil ...

Combine Two Values within Model for Dropdown Menu

I am currently facing a situation where I have a select box that displays a list of users fetched from the backend. The select box is currently linked to the name property of my ng model. However, each user object in the list also contains an email proper ...

Create a versatile generic object using TypeScript

Looking to create a versatile onFilterChange helper function that works for all filters, eliminating the need to write it out separately each time. However, I've hit a snag: // helper.ts export function onFilterChange(prevState: Record<string, any& ...

Creating a generic that generates an object with a string and type

Is there a way to ensure that MinObj functions correctly in creating objects with the structure { 'name': string }? type MinObj<Key extends string, Type> = { [a: Key]: Type } type x = MinObj<'name', string> Link to Playgr ...

Optimizing Performance in Firebase Cloud Functions - Defining Functions for Efficiency

Currently, I am organizing the code in my index.ts by creating simple line function definitions like: HTTP Example export const demoHttpApp = functions.https.onRequest( (req, resp) => new DemoHttpClass(req, resp).run() ); Real-Time Database Example ...

Sending the :id parameter to the Service component

In the early days of my Angular journey, I have a simple question. Currently, I am utilizing the WordPress REST API to showcase a list of posts from a specific category by using posts?categories={ID HERE}. However, I am facing an issue in passing the ID f ...

Passing data from a container component to a Redux Form component in React with TypeScript

One of my Form container components looks like this: class PersonalDetailContainer extends React.Component<PropTypes> { onSubmit = async (fields: PersonalFields) => { this.props.savePersonalDetail(fields); }; render(): JSX.Element { ...

The modal popup feature is dysfunctional within the hierarchical component structure of angular-bootstrap-md

In my project, there is a structured hierarchy of components that includes: Agent task-list (utilizing the shared task-list-table component) task-type (as a separate component) preview-task (a modal component) agent.component.html (where task-type, ta ...

Angular does not wait for the backend service call response in tap

Does anyone have a solution for subscribing to responses when the tap operator is used in a service? edit(status) { dataObj.val = status; // call post service with status.. this.service .update(dataObj) .pipe(takeUntil(this._n ...

How to minimize xAxes labels in Chart.js

As a newcomer to Chart.js, I am encountering some challenges. My goal is to create a bar chart that displays hourly information. However, when attempting to show data for a week, a month, or an extended period, I face issues with reducing the labels on the ...

Preserve the checkbox state upon refreshing the page

I am facing an issue with keeping the checkbox state saved after a page reload. Currently, I have stored my unchecked checkboxes in localStorage, but I am unsure about what steps to take next. In simple terms, I want the checkbox to remain unchecked when I ...

position the tooltip within the ample available space of a container in an angular environment

In my editor, users can create a banner and freely drag elements within it. Each element has a tooltip that should appear on hover, positioned on the side of the element with the most space (top, left, bottom, right). The tooltip should never extend outsid ...

What is the recommended TypeScript type for the NextJS _app.tsx Component and pageProps?

Take a look at the default _app.tsx code snippet from NextJS: function MyApp({ Component, pageProps }) { return ( <Component {...pageProps} /> ) } The issue arises when transitioning to TypeScript, as ES6Lint generates a warning indicating t ...

Example of Using Bluebird in a Chain of Catch and Then Functions

I am struggling to understand how promises work in my code flow setup. I want to create a register function with multiple steps, but I'm unsure of what is achievable and what is not. Imagine I have a register function where I need to: register a u ...