Incorporate a hyphen to every offspring within a multi-tiered category hierarchy using JavaScript

I have an array structure that represents categories with multiple levels:

[
        {
            "id": 5,
            "parent_id": 3,
            "name": "Fruits",
            "parent": {
                "id": 3,
                "parent_id": 1,
                "name": "Vegetables and fruits",
            }
        },
        {
            "id": 6,
            "parent_id": 3,
            "name": "Vegetables",
            "parent": {
                "id": 3,
                "parent_id": 1,
                "name": "Vegetables and fruits",
            }
        },
        {
            "id": 3,
            "parent_id": null,
            "name": "Vegetables and fruits",
            "parent": null
        },
]

My goal is to prepend a '-' (dash) to each child category based on its level within the hierarchy. For example, first-level categories get one dash '-', while third-level ones get two dashes '--'.

Is there a way to update the array's structure like this?

I've attempted some code snippet below:

const updateData = (data) => {
    return data.map(item => {
        if (item.parent_id != null) {
            // Recursive logic might be needed here
            item.name = `-${item.name }`;
        }
        return item;
    })
}           

Expected output after modification:

Vegetables and fruits
Drinks
-Fruits
-Vegetables
--Tomatoes
--Potatoes

Answer №1

One approach is to define a function called getLevel, which can determine the level of any given node. This information can then be used to determine how many dashes should be included in the output:

let nodes = [ { "id": 5, "parent_id": 3, "name": "Fruits", "parent": { "id": 3, "parent_id": 1, "name": "Vegetables and fruits", } }, { "id": 6, "parent_id": 3, "name": "Vegetables", "parent": { "id": 3, "parent_id": 1, "name": "Vegetables and fruits", } }, { "id": 3, "parent_id": null, "name": "Vegetables and fruits", "parent": null }, { "id": 7, "parent_id": 5, "name": "Strawberries", "parent": { "id": 5, "parent_id": 3, "name": "Vegetables and fruits", } }, { "id": 8, "parent_id": 6, "name": "Tomatos", "parent": { "id": 6, "parent_id": 3, "name": "Vegetables and fruits", } }, ]

// Pre-compute object / parent mapping
const parentMap = nodes.reduce((acc,obj) => { 
    acc[obj.id] = obj?.parent;
    return acc;
}, {})

// Determine the node's level based on its parent id.
function getLevel(parentMap, obj) {
     let level = 0;
     while (parentMap[obj.id]) {
         level++;
         obj = parentMap[obj.id] 
     }
     return level;
}

const finalResult = nodes.map(obj => {
    level = getLevel(parentMap , obj);
    return { ...obj, level} ;
})
.sort((a,b) => a.level - b.level)
.map(obj => '-'.repeat(obj.level) + obj.name)

console.log('Final Output:', finalResult)

Answer №2

To simplify the process, consider converting your input into a list of trees to handle separate roots efficiently. This transformation allows for easy recursive traversal and printing. Below is an example implementation:

const input = [
        {
            "id": 5,
            "parent_id": 3,
            "name": "Fruits",
            "parent": {
                "id": 3,
                "parent_id": 1,
                "name": "Vegetables and fruits",
            }
        },
        {
            "id": 6,
            "parent_id": 3,
            "name": "Vegetables",
            "parent": {
                "id": 3,
                "parent_id": 1,
                "name": "Vegetables and fruits",
            }
        },
        {
            "id": 3,
            "parent_id": null,
            "name": "Vegetables and fruits",
            "parent": null
        },
]


function buildTrees(input) {
  const mapping = new Map()

    // Create a map based on item id
  input.forEach(i => {
    delete i.parent // exclude parent details
    i.children = []
    mapping.set(i.id, i)
  })

    // Add each node to its parent's children list
  input.filter(i => i.parent_id !== null).forEach(i => {
    mapping.get(i.parent_id).children.push(i)
  })

    // Return the root nodes
  return input.filter(i => i.parent_id === null).map(i => mapping.get(i.id))
}

function printTree(tree, depth) {
    // Recursive function to print tree names
    console.log('-'.repeat(depth) + ' ' + tree.name)
  tree.children.forEach(child => printTree(child, depth + 1))
}

const trees = buildTrees(input)

trees.forEach(tree => printTree(tree, 0))

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 is the best way to assign IDs dynamically within an onclick function in Django?

Recently, I started working with Django and I have a total of 8 cards. Each card contains an ID, content, and image path that I dynamically pulled from the database. My goal is to store the information of the clicked card in a JavaScript object and then pr ...

Having trouble with understanding the usage of "this" in nodejs/js when using it after a callback function within setTimeout

It's quite peculiar. Here is the code snippet that I am having trouble with: var client = { init: function () { this.connect(); return this; }, connect: function () { var clientObj = this; this.socket = ...

Using a PHP foreach loop to filter an array based on array keys

Forgive me for asking a possibly obvious question, but I'll ask it anyway... I'm using a loop to generate filtered results from a multi-dimensional array. foreach ($myArray as $k => $v) { if (array_keys($v, 'today')) { ...

Create a CSV file through an MVC Web API HttpResponse and retrieve it using AngularJS for downloading

I am attempting to create a CSV file from my web API and retrieve that file using angularjs. Below is an example of my API controller: [HttpPost] public HttpResponseMessage GenerateCSV(FieldParameters fieldParams) { var output = new byte[ ...

When setting a background-image with jQuery, the background will only change once

I am currently attempting to dynamically set the background-image property of an element based on the value of a variable. Oddly enough, it only seems to work the first time. Once I switch to ready, the background image stops changing altogether. The image ...

Guide to extracting the outcomes of a promise array and populating them into a new array using Protractor

I am facing a challenge with transferring data from an array of promises generated by the following code: element.all(by.repeater('unit in units')). It seems to be difficult for me to store this data into another array: element.all(by.repeater(& ...

Enhance cart items in a shopping cart using Redux

I'm encountering an issue with my reducer's functionality in adding an item to the cart and increasing its quantity if it already exists. Currently, instead of updating the existing quantity, my code simply adds another "quantity" with a value of ...

No pathways can be established within Core UI Angular

I've been attempting to use the router link attribute to redirect to a new page, but instead of landing on the expected page, I keep getting redirected to the dashboard. Below is an overview of how my project's structure looks: [![enter image de ...

How can I pull the account creation date stored in MongoDB and display it using Handlebars?

Currently in my development, I am utilizing MongoDB, NodeJS, and Handlebars. My challenge is to convert the user.id into a timestamp and then display this timestamp on my HTML page. At present, I can display the user.id by using {{ user.id }} in my code, ...

Retrieving and submitting text in a NodeJS environment

I'm attempting to retrieve text from an API that only provides a string of text ((here)), but I am encountering difficulties in displaying it accurately. When I try to post it, the output shows as [object Response], and the console.log is not showing ...

Unable to invoke JS function in Drupal 5 file

In my current project using Drupal 5, I have a specific .js file that is included using the code: drupal_add_js(drupal_get_path('module','MTM')."/include/JS_form.js"); Additionally, there is an element on the page: <a onclick="MTM ...

Retrieving object key value from an array using Underscore.js

Hey there, I'm facing a challenge where I need to extract the values of wave1 and wave2 from an array using underscore.js. array = [{"id":1,"name":"Monoprix", "pdv":16,"graph":[{"wave1":22,"wave2":11}]} ; I attempted the following: $scope.wave1 = a ...

How to identify and assign labels to elements in a numpy group

I have a solid grasp on how to assign labels to elements in one input array as shown below: arr_value = np.array([0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 2, 1, 1, 1, 1]) arr_res_1 = np.array([0, 1, 2, 3, 3, 4, 5, 6, 7, 7, 8, 9, 9, 9, 9]) # treat zeros in arr_value ...

NodeJS JSONStream causing memory exhaustion issue

I've encountered an issue while trying to stream a large JSON file (94mb in size) from an HTTP request to a local file using the JSONStream library in NodeJS. Despite setting a memory flag of 256mb with node --max-old-space-size=256 .\main.js, th ...

several parameters for the `ts-node -r` options

Can I include multiple require statements in the package.json script before running with ts-node -r? "scripts": { "start": "ts-node -r dotenv/config newrelic src/index.ts", } I'm having trouble adding both "dotenv/config" and "newrelic" si ...

Toggle a v-if variable within the created() lifecycle hook in the VUE 3 framework

I'm working on a component with 3 buttons, where initially only 2 are visible. <template> <button v-show="!showLogout" @click="login('google', 'profile, email')"> Google </button> &l ...

The module 'react/lib/React' could not be located within the file 'ReactTestUtils.js'

Setting up unit-tests for a React Native project using ReactTestUtils is causing an issue with npm test. The error message I receive is: Cannot find module 'react/lib/React' from 'ReactTestUtils.js' I suspect that my dependencies are ...

Creating a JavaScript popup script from scratch

I'm looking to create a script using only JavaScript that will display a popup when the page loads. The background should be faded and I want to include custom content in the popup. I don't want to utilize CSS or HTML directly. Embed the CSS ...

Before the file upload process is finished, the progress of tracking Angular files reaches 100%

I am currently developing a service that is responsible for uploading a list of files to a backend server. createFiles(formData: any, userToken: string): Observable<any> { const headers = new HttpHeaders({'Authorization': 'Bearer ...

Retrieving Information using Ajax in Laravel with Form Inputs

I'm currently working on an auto-complete form that requires users to only input the ID, after which all relevant information related to that ID will appear in another field. My code utilizes ajax and Laravel 5.4. The data is successfully retrieved an ...