Using Ramda, learn how to transform a flat list into a hierarchical one

Looking to transform the given list into a hierarchical structure with nested children fields. The 'parentId' attribute has been omitted for clarity, as it will be used in the transformation process using Ramda's immutable behavior.

const x = [
  {
    id: 1,
    parentId: null,
    name: 'Top 1'
  },
  {
    id: 2,
    parentId: 1,
    name: 'Middle'
  },
  {
    id: 3,
    parentId: 2,
    name: 'Leaf'
  },
  {
    id: 4,
    parentId: null,
    name: 'Top 2'
  },
];

The desired output:

const result = [
    {
        id: 1,
        name: 'Top 1',
        children: [
            {
                id: 2,
                name: 'Middle',
                children: [
                    {
                        id: 3,
                        name: 'Leaf',
                        children: []
                    }
                ]
            }
        ]
    },
    {
        id: 4,
        name: 'Top 2',
        children: []
    }
];

Answer №1

When applying a pure Ramda / point-free / functional approach, the following steps can be taken:

const overChildren = over(lensProp('children'))
const findChildren = completeList => parent => filter(child => child.parentId === parent.id, completeList)

const assocChildren = completeList => map(pipe(
  parent => assoc('children', findChildren(completeList)(parent), parent),
  parent => overChildren(assocChildren(completeList), parent)
))

const removeParentIds = map(pipe(
  dissoc('parentId'),
  overChildren(x => removeParentIds(x))
))

const isTop = compose(isNil, prop('parentId'))
const keepOnlyTop = filter(isTop)

const hierarchize = completeList => pipe(
  assocChildren(completeList),
  keepOnlyTop,
  removeParentIds
)(completeList)

hierarchize(list)

Please note that the focus here is on enhancing readability and maintainability rather than optimizing performance.

Answer №2

Responding in this chatroom, I came up with the following solution:

const generateTree = items => {
  const hierarchy = reduce(
    (index, item) => item.parentId in index 
      ? assoc(item.id, [], assoc(item.parentId, append(item.id, index[item.parentId]), index))
      : assoc(item.id, [], index)
    , {}, 
    items
  ) //=> Example: {"1":[2],"2":[3],"3":[],"4":[]} 
  const index = map(head, groupBy(prop('id'), items)) //=> Example: {"!": <item1>, "2": <item2>, ...}
  const createNode = id => dissoc('parentId', assoc('children',  map(createNode, hierarchy[id]), index[id]))

  return map(createNode, pluck('id', filter(item => item.parentId == null, items)))
}

This approach involves multiple iterations through the data and using groupBy may seem a bit unconventional, but it gets the job done.

The nested assoc calls are not very elegant. Refactoring them into compose/pipe might enhance readability.

You can test this code on the interactive platform.

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

Quickly Share Documents within a Folder

I currently have a server set up with the following file structure: - index.js /client -index.html -about.html -signup.html -style.css Within the index.js file, I have implemented an express server. When the user navigates to /, it should display the ...

Retrieving information from CRM online utilizing the Web API

Recently, I developed a webpage to serve as a survey for end users to provide feedback on the helpdesk technician's performance in resolving tickets. The webpage was constructed using HTML, CSS, JS, and PHP. Currently, the page requires access to two ...

Enhancing the node module of a subpackage within Lerna: A step-by-step guide

I recently integrated lerna into my workflow to streamline the installation of all node modules for multiple sub packages with just one command. Currently, I'm only utilizing the lerna bootstrap feature. Here's a snippet from my lerna.json: { & ...

Having trouble getting Emberjs to properly handle an Ajax POST request

Currently, my goal is to send data to the database using Ajax POST. To start off, I have an HTML form as follows: <script type="text/x-handlebars" id="project"> <div class="row"> <div class="span6"> <form class ...

Extract data from the HTML source code in JavaScript and transfer it to a personalized JavaScript variable within Google Tag Manager

Running an e-commerce website on Prestashop and facing an issue with data layer set up. Instead of a standard data layer, the platform generates a javascript variable called MBG for Enhanced E-Commerce implementation. <script type="text/javascript"> ...

Tips on containing the reach of a function in JavaScript/jQuery

I have a scenario where I have multiple .js files linked on the page, each containing functions with the same name. For example: first.js function DisplayContent(data,$target) // data is string { $target.html('<span>'+ data +'&l ...

Unable to access MongoDB documents using NodeJS/Express

I can't seem to figure out why I am not receiving any results from a GET call I am testing with Postman. Let's take a look at my server.js file: const express = require("express"); const mongoose = require("mongoose"); const ...

"Exploring the Magic of Jquery's Fadein() Animation

i have experience with writing code for a jQuery fadein effect. imagine I have an HTML element stored in a variable, like this: var sHtml="<div>Other content</<div><div id='frm'>hello</<div>"; modal.load(jQuery(sHt ...

An application running on Node.js/Express and hosted on Digital Ocean encounters the "Cannot get /" error message

Despite searching extensively online and sifting through numerous solutions to different scenarios, I remained unable to find a fix that resolved my issue. When attempting to run my server.js file locally, everything operates smoothly. However, upon transf ...

A error was encountered stating that the formValidation function is not defined when the HTML form element is submitted

Having trouble calling a function and receiving an error message. How can I resolve this issue? The error message reads: index.html?email=&gebruikersnaam=&wachtwoord=&submit=Submit:1 Uncaught ReferenceError: formValidation is not defined at HT ...

The declaration file for the 'react' module could not be located

I was exploring Microsoft's guide on TypeScript combined with React and Redux. After executing the command: npm install -S redux react-redux @types/react-redux I encountered an error when running npm run start: Type error: Could not find a decla ...

The method window.scrollTo() may encounter issues when used with overflow and a height set to 100vh

Suppose I have an HTML structure like this and I need to create a button for scrolling using scrollTo. However, I've come across the information that scrollTo doesn't work well with height: 100vh and overflow: auto. What would be the best way to ...

transfer jquery element using jquery ajax

How can I send a jQuery object to a PHP function using POST? When I stringify the object, I get the following output: [ { "id": "701", "user_id": "2", "playlist": "ukg%20garage", "tracks": "5", "thumbnail": "Coldplay.jpeg", "crea ...

Is the Utilization of Inline JavaScript in HTML Attributes by Angular considered a "good practice"?

While going through the Angular tutorials, I found a lot to like. However, I couldn't help but wonder if "ng-click" is not essentially just an inline onClick function. My understanding was that the JavaScript community frowned upon using inline JavaSc ...

The width of Material UI Grid decreases every time it is re-rendered

I'm attempting to display a list of 25 words in a 5x5 grid using the MUI Grid component. The grid is structured as a <Grid container direction="column"> with five <Grid item> elements. Each <Grid item> contains: <Grid co ...

Arranging an array of objects by a specific property in an Angular controller with the help of $filter

In my data set, there is an array of objects referred to as $scope.segments, which looks like this: [ { "_id": "55d1167655745c8d3679cdb5", "job_id": "55d0a6feab0332116d74b253", "status": "available", "sequence": 1, "body_original": " ...

I'm puzzled about what could be behind this error message Error [ERR_HTTP_HEADERS_SENT], especially since I've only sent the response header once. How can I figure out the cause

Here is a snippet of code from my routes file: router.get('/api/', async function(request, response){ let entries = await Entries.find({}, function(error){ if(error) console.log(error); }); let catArray = []; entrie ...

Removing a field from a collection using firebase-admin: Tips and tricks

I currently have a collection stored in Firebase Realtime Database structured like this: My requirement is to remove the first element (the one ending with Wt6J) from the database using firebase-admin. Below is the code snippet I tried, but it didn' ...

Upload file via AJAX immediately after downloading it (no need for storage)

Currently, I am in the process of developing a JavaScript script to safeguard an old game development sandbox website from being discarded by its owners. In order to prevent the loss of all the games on the site, I have managed to create a script that allo ...

Transfer the data-url attribute to the jQuery ajax URL

I am facing an issue with my form which includes a data-attribute holding a URL to an API JSON file: <form class="product" action="#" data-url="dist/scripts/main.js"> [...] </form> My goal is to transfer the URL from the data attribute to ...