Ways to access the offspring of a heavily nested array in JavaScript

I am facing a data challenge with the following structure:

  const data = [
    {
      name: "Car",
      id: "19",
      count: "20",
      depth: "1",
      children: [
        {
          name: "Wheel",
          id: "22",
          count: "3",
          depth: "2",
          children: [
            {
              name: "Engine",
              id: "101",
              count: "1",
              depth: "3",
              children: [
                {
                  name: "Engine and Brakes",
                  id: "344",
                  count: "1",
                  depth: "4",
                  children: []
                }
              ]
            }
          ]
        }
      ]
    },
    {
      name: "Bike",
      id: "3",
      count: "12",
      depth: "1",
      children: [
        {
          name: "SpeedBike",
          id: "4",
          count: "12",
          depth: "2",
          children: []
        }
      ]
    }
  ];

My goal is to filter out specific categories based on their IDs, as shown below:


[
  {
     name: "Engine and Brakes",
     id: "344",
     count: "1",
  },
  {
    name: "SpeedBike",
    id: "4",
    count: "12",
   }
]

If no category ID is provided, I need to display both the parent and its direct children by default:

[
 {  
    name: "Car",
    id: "19",
    count: "20"
 },
 {
    name: "Wheel",
    id: "22",
    count: "3"
 },
 {
    name: "Bike",
    id: "3",
    count: "12",
 },
 {
    name: "SpeedBike",
    id: "4",
    count: "12"
   }
]

In cases where the passed category ID has no children, an empty array should be returned:

[]

I aim to solve this without utilizing for, foreach, or while loops. I have attempted using map and filter methods without success. Seeking advice on the best approach for this scenario. Thank you!

Note: I am working with JavaScript (js) and TypeScript (ts).

The nested array can have multiple deep levels.

Answer №1

There isn't a built-in function for this task. Instead, we can approach it using two methods: iterative and recursive.

The recursive method requires extra space of O(logN) for call stack size; hence, the iterative method is preferred.

Algorithm:

  • If category ids are provided:
    • Fill the stack with categories from the first level
    • While the stack is not empty:
      • Remove a category from the stack
      • If the category id is in the desired ids array:
        • Iterate through the category's children and get the required properties
      • Add children of the category to the stack
  • else:
    • Loop through the first level.
    • Add the parent and children of the parent to the stack.

Type for category:

type Category = {
  name: string;
  id: string;
  count: string;
  depth: string;
  children: Category[];
};

Implementation:

const getCategoriesChildren = (
  categoryIds: Category['id'][],
  categories: Category[],
) => {
  const foundChildren: Pick<Category, 'id' | 'count' | 'name'>[] = [];

  if (categoryIds.length === 0) {
    return categories.reduce<Pick<Category, 'id' | 'count' | 'name'>[]>(
      (acc, category) => {
        acc.push(mapCategory(category), ...category.children.map(mapCategory));
        return acc;
      },
      [],
    );
  }

  const stack = [...categories];

  while (stack.length) {
    const category = stack.pop();
    if (!category) continue;
    if (categoryIds.includes(category.id)) {
      foundChildren.push(
        ...category.children.map((childCategory) => ({
          name: childCategory.name,
          id: childCategory.id,
          count: childCategory.count,
        })),
      );
    }
    stack.push(...category.children);
  }

  return foundChildren;
};

Usage:

// [{
//   "name": "SpeedBike",
//   "id": "4",
//   "count": "12"
// }, {
//   "name": "Engine and Brakes",
//   "id": "344",
//   "count": "1"
// }] 
console.log(getCategoriesChildren(['101', '3'], data));

// [{
//   "name": "Car",
//   "id": "19",
//   "count": "20"
// }, {
//   "name": "Wheel",
//   "id": "22",
//   "count": "3"
// }, {
//   "name": "Bike",
//   "id": "3",
//   "count": "12"
// }, {
//   "name": "SpeedBike",
//   "id": "4",
//   "count": "12"
// }]
console.log(getCategoriesChildren([], data));

Take a look at the playground here!

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

The power of Vue reactivity in action with Typescript classes

Currently, I am working on a Vue application that is using Vue 2.6.10 along with Typescript 3.6.3. In my project, I have defined a Typescript class which contains some standard functions for the application. There is also a plugin in place that assigns an ...

The attribute 'title' cannot be found within the type 'IntrinsicAttributes & IProps'

Hello, I am currently facing an issue where I am trying to hide an item in a different view using interfaces. The specific error I am encountering is mentioned in the title of this query. Let me provide you with part of the code to give you more context: ...

An exception is thrown by TypeScript's readline.createInterface function

My current project involves creating an electron app. I am facing an issue in the main.ts file where I have constructed a seemingly simple class with a constructor that is not running as expected. The problem arises when the call to readline.createInterfac ...

Are there any other commands similar to "now dev" available on Vercel?

"now dev" is not being recognized as a command on my Windows 10 system with Vercel, JavaScript, Node.js, and MongoDB. I am trying to test my API using Postman, but when I enter the "now dev" command in the command prompt, it does not wo ...

Tips for observing the return value of a function in JavaScript

When dealing with an object in a browser, each property has a getter and setter that can be overridden in order to monitor the attribute. But what about the return value of a function? In AngularJS, we can use 'ng-show' to control the visibility ...

PHP 5.5.12 Unauthorized access to array element leads to illegal string offset

I am encountering an "Illegal string offset" error while trying to access a specific key in an array: Using PHP version 5.5.12 This line is causing the error: $avg = $season['avg']; Below is the output of var_dump($season): array (size=38) ...

Exploring OpenMP Shared Arrays in C: Analyzing Reduction versus Atomic Update Issues

Trying to pinpoint the issue with this OpenMP task example has been a challenge. To provide some context, y is a shared array of considerable size and rowIndex is not unique to each task. This means that multiple tasks may be trying to increment the value ...

What is the best method for serializing data associated with the current item in a jQueryUI sortable list?

Currently working with jQueryUI 1.8.14, I am interested in creating a sortable list and saving any changes to item positions in my application database whenever a user adjusts an item's position. To achieve this, my plan is to retrieve and serialize ...

This element is not compatible for use as a JSX component

I have created a React component which looks like this import React from 'react'; import { ECOTileSummary } from './ECOTileSummary'; import { TileSummary } from './TileSummary'; interface SuperTileSummaryProps { date?: s ...

The sequence of methods firing seems to be jumbled up

My attempt to utilize JSON for returning an array that can be manipulated later is encountering some issues when using AJAX to return a JSON encoded string. When debugging with alerts, it seems like the alerts are firing in the incorrect order. Check out ...

Improve rotation smoothness in panorama using arrow keys with Three.js

In order to create an interactive panorama application using three.js based on the example provided Panorama, I needed to incorporate rotation functionality with arrow keys (left and right arrow keys). I implemented an event listener to achieve this, adjus ...

Is it possible in Typescript to restrict object creation to Record<string, T> while still being able to access the defined

Suppose I define a type called Param as follows: type Param = { field: string; active: boolean; } I then use this type to create a record with keys of type string and values of type Param, like so: const params: Record<string, Param> = { foo: { f ...

Create a 3D rendering of an .obj file using Three.js without relying on the

I have a challenge of drawing a .obj file without loading it. Let's consider an example where the .obj file contains the following content: v 0.1 0.2 0.3 v 0.2 0.1 0.5 vt 0.5 -1.3 vn 0.7 0.0 0.7 f 1 2 3 By reading and parsing this file, I am able to ...

Using angularjs, populate a dropdown menu by enclosing the options in curly braces

Utilizing the curly braces syntax interpolation in AngularJS allows us to connect data from the model to the view. This technique is typically used for displaying text. Is there a way to populate a dropdown list using the {{}} syntax? ...

Angular getElementById is a useful method for accessing and manipulating elements

I am using asp.net and angular 7 to retrieve data saved by a form with a specific ID. The data is successfully stored in local storage but is not being displayed on the front-end for the user. No error messages are being shown. Can anyone assist me with th ...

Javascript promises executing in a mixed-up sequence

Utilizing Javascript's native Promise, I created a modified version of fs.readFile called readFileAsync. This function reads and parses a JSON file, returning the object when resolving. function readFileAsync(file, options) { return new Promise(fun ...

How about utilizing a map with arrays for this object?

Currently, I am delving into the realms of JS and TypeScript with a focus on generating separate React components for specific items. Allow me to introduce you to an example object: var regions = { NA: ["US", "ABC1"], EU: ["GB", "LX", "IT"], F ...

Simple steps to manipulate HTML content within a span element using JavaScript

I'm currently working on an online bus seat booking system and I've hit a roadblock with one feature. When a user selects more than one bus seat, the corresponding seat numbers should be displayed in a span or div, separated by commas. If the use ...

"Having trouble implementing sorting functionality on a click event in a React application with Material-UI table

Default behavior displays data in ascending order. Clicking on the table header should toggle between descending and ascending orders. Load Data in ascending order -> On click, change to descending order -> Again on click, change to ascending -> ...

Prevent Buttons from Being Clicked While Another is Active in Angular

I am facing a challenge with my Angular functions that enable search features. My goal is to allow only one feature to be selected at a time. /* Trauma Search functionality */ $scope.traumaSearch = false; $scope.traumaText = "Trauma Center"; $scope.togg ...