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

Tips for enhancing the contents of a single card within a react-bootstrap accordion

Currently, I am facing an issue with my columns expanding all cards at once when utilizing react-bootstrap accordion. My goal is to have each card expand individually upon clicking on its respective link. However, I am encountering difficulties in implem ...

Problems with Ajax functionality

Excuse my rusty JavaScript skills but I'm attempting to use an AJAX call to a PHP file, pass it a plan type, and then determine if there are enough available slots for the plan. If so, return true; otherwise, false. Below is the Form in XHTML: <fo ...

What could be causing the issue with retrieving HTTP requests in Nest.js even after configuring the controller?

After the individual who departed from the company utilized Nest.js to create this server-side system. They established the auth.controller, auth.service, auth.module, auth-token, jwt.strategy, and jwt-payload, with everything properly configured. I verifi ...

C# variable value not being retrieved by jQuery

When I implemented the jQuery scripts in my .aspx file, everything worked perfectly. Here is the code snippet: $(function() { var availableTags = [ "ActionScript", "AppleScript", "Asp", "BASIC", "C", "C+ ...

Ways to eliminate redundant older React components within a different library

Having trouble with material-ui in my React project as I encounter this error. Error: Invalid hook call. Ensure hooks are only called inside the body of a function component. Check for: Possible mismatch of React and renderer versions (i.e., React DOM) ...

"An error occurs when attempting to clear Rad Grid data with javascript: htmlfile: Invalid argument thrown

Hello, I am currently developing an ASP.NET application. I am facing an issue where I need to clear the data in a Rad grid upon button click using JavaScript. The code snippet that I have attempted for this is as follows: document.getElementById(&a ...

Select a Button to randomly choose another Button

I am currently developing a dynamic Bootstrap OnePage-Website using HTML, CSS, and JavaScript. The highlight of this website is the Team section where users can book appointments with one of three team members by clicking on a corresponding button beneat ...

Using jQuery to append text after multiple element values

I currently have price span tags displayed on my website: <div> <span class="priceTitle">10.00</span> </div> <div> <span class="priceTitle">15.00</span> </div> <div> <span class="priceTitle">20.0 ...

Converting an object to an array with the help of jQuery

I have been working with a json file and created an object using jquery's $.get method. Here is what it looks like when I log it: console.log(array); [Object, Object, Object, Object, Object, Object, Object, Object] 0 : Object country ...

Is there a way to determine if a table cell contains overflowing text compared to another cell?

Is there a way to detect if the content of the fourth td overflows from the second td? I am looking for a method to determine which td has overflowed text and identify which td's text is overflowing. What approach can be used to determine which td i ...

What is the best way to play a video from a certain time point in a React application?

How can I make my component automatically play from a specific time like 00:07:12,600 instead of starting from the beginning? import style from './Hero.module.css'; import Image from 'next/image'; import ReactPlayer from 'react-pla ...

Understanding the variations in behavior of the history.go(-1) method across various browsers

Is history.go(-1); consistent across all browsers? I've noticed different behaviors on various browsers. In my code, I have a line similar to javascript:history.go(-1); On the first page, there are three checkboxes. Users can only select two of them ...

Concealing a form after submission using JavaScript

After submitting the form, I am attempting to hide it and display a loading GIF. I've experimented with various methods, including changing ClassName to Id, but haven't had success. This is for a school project, and I've spent a significant ...

Ensuring form field accuracy through server-side validation using Ajax - Mastering the art of Ajax

I am in need of implementing Ajax to validate my form fields, currently I have a javascript validation set up. Is it possible to reuse my existing javascript code and integrate Ajax for form validation? HTML Form <form method="post" action="ajax/valid ...

How can I link to a different field in a mongoDB Schema without using ObjectID?

I have created two schemas for books and authors: const bookSchema = new mongoose.Schema({ title: String, pages: Number, description: String, author: { type: mongoose.Schema.Types.ObjectId, ref: 'Author' } }); const Book = mongoose.model ...

Create the key's value in a dynamic manner within localforage

When utilizing localForage to store data offline, I encountered an issue with generating unique key values for each form submission. My goal is to have the key value generated dynamically as 'activity_1', 'activity_2', 'activity_3& ...

Tips for incorporating AngularJS with dynamically added elements

Essentially, I have a structure similar to this example The formula for every cell in the last column should be: value[i][2] = value[i-1][2] + value[i][0] - value[i][1] I'm currently facing two issues. The first one arises when attempting to progra ...

Guide on extracting a JavaScript string from a URL using Django

How can I extract "things" from the JavaScript URL "/people/things/" without any unnecessary characters? I've attempted inefficient methods like iteration, but struggle with removing the undesired parts of the string, leading to slow performance. Th ...

Conceal or reveal input elements with a toggle function in jQuery by utilizing

When the user chooses an option from a dropdown list, I want to display or hide different input fields accordingly. I attempted to use toggle with boolean values, but it doesn't seem to be working as expected. I expect that if I send 'true' ...

When using Javascript, the click function is returned as not being a valid function

I am working on a project with two PHP files - index.php and loadimages.php. The index.php page contains a thumbnail gallery and a canvas. The images in the thumbnail gallery are populated by loadimages.php. Here is a snippet of the code from loadimages.ph ...