Reorganize the data in a different manner (collections of items instead of individual items linked to groups)

Is there a way to create a function that converts data from `DevicesType` to `GroupsType` below? Instead of having a list of devices showing the groups they belong to, I need a list of groups with their respective devices.

type DevicesType = {
  id: string
  name: string
  status: "OK" | "FAULTY"
  groups: {
    id: string
    name: string
  }[]
}[]
type GroupsType = {
  id: string
  name: string
  devices: {
    id: string
    name: string
    status: "OK" | "FAULTY"
  }[]
}[]

For example:

const devicesFromBackend: DevicesType = [
  {
    id: "d1",
    name: "device 1",
    status: "OK",
    groups: [
      {
        id: "g1",
        name: "group 1"
      },
      {
        id: "g2",
        name: "group 2"
      }
    ]
  },
  {
    id: "d2",
    name: "device 2",
    status: "FAULTY",
    groups: [
      {
        id: "g2",
        name: "group 2"
      },
      {
        id: "g3",
        name: "group 3"
      }
    ]
  }
];

I aim to achieve this in order to display tables for each group with a list of their corresponding devices.

Answer №1

Here is a potential solution that functions without any external dependencies

function convertDevicesToGroups(devices: DevicesType): GroupsType {
    // Separating groups
    const groups = devices.map((d) => d.groups).                            // Extracting groups
        reduce((g, i = []) => i.concat(...g)).                              // Converting from 2D to 1D
        filter((g, i, self) => i === self.findIndex((e) => e.id === g.id)); // Removing duplicates


    return groups.map((group) => {
        const belongDevices = devices.
            filter((d) => d.groups.find((g) => g.id === group.id)).        // Filtering devices that belong to a group
            map((d) => ({ id: d.id, name: d.name, status: d.status }));    // Extracting required properties

        return {
            ...group,
            devices: belongDevices,
        }; 
    });
}

Go to the playground for the full code and an example.

Additionally, I suggest some minor enhancements to your types for better readability.

type Group = {
  id: string
  name: string
  devices: {
    id: string
    name: string
    status: "OK" | "FAULTY"
  }[]
}

type Groups = Group[]

type Device = {
  id: string
  name: string
  status: "OK" | "FAULTY"
  groups: {
    id: string
    name: string
  }[]
}

type Devices = Device[]

It would be beneficial to eliminate the implicit circular dependency, but that would involve a more extensive refactoring unrelated to your current issue

Answer №2

Below is a potential JavaScript implementation to achieve the desired outcome:

const devices = [
  {
    id: "d1",
    name: "device 1",
    status: "OK",
    groups: [
      {
        id: "g1",
        name: "group 1"
      },
      {
        id: "g2",
        name: "group 2"
      }
    ]
  },
  {
    id: "d2",
    name: "device 2",
    status: "FAULTY",
    groups: [
      {
        id: "g2",
        name: "group 2"
      },
      {
        id: "g3",
        name: "group 3"
      }
    ]
  }
];

const getDeviceGroups = (arr = devices) => (
  Object.values(
    arr.reduce(
      (aggregated, {groups, ...rest}) => ({
        ...aggregated,
        ...groups.reduce(
          (accumulator, {id, name}) => ({
            ...accumulator,
            [id]: id in aggregated
              ? {
                ...aggregated[id],
                devices: aggregated[id].devices.concat([{
                  ...rest
                }])
              }
              : {id, name, devices: [{...rest}]}
          }),
          {}
        )
      }),
      {}
    )
  )
);

console.log(getDeviceGroups());

The approach is detailed within the code comments for reference. Additional explanations can be provided upon request.

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

Re-establishing connections in the force-directed graph model

Just getting started with d3.js and I'm currently attempting to reconnect paths between nodes on a force graph. Here is an example image of what I am trying to achieve: https://i.sstatic.net/knvH9.jpg I want to be able to drag the red circle and hav ...

Troubleshooting problem with video recording functionality using HTML 5 media streams

My HTML 5 code for video recording and uploading is encountering a JavaScript error when the start button is clicked. The error messages I am receiving are: "TypeError: webcamstream.record is not a function streamRecorder = webcamstream.record();" "TypeE ...

Choosing Between Methods and Computed Properties in Vue.js

Can you explain the primary distinction between a method and a computed property in Vue.js? I'm finding it tricky to differentiate between the two as they appear quite similar. ...

Tips for altering the color of the MUI table sort label icon:

Does anyone know how to change the color of the table sort label icon from gray to red? I am having trouble figuring it out. Any recommendations or suggestions would be greatly appreciated. Here is the code I have been trying to work with: <TableSortL ...

Express.js encountered a FetchError due to receiving an invalid JSON response body from https://api.twitter.com

I am currently working on a project that involves getting the tweet id as form input and using the Twitter API to retrieve data about that specific tweet. However, I have encountered an issue where the JSON data is not being returned properly. router.post( ...

The Highchart formatter function is being called twice on each occasion

My high chart has a formatter function that seems to be running twice. formatter: function() { console.log("starting formatter execution"); return this.value; } Check out the Fiddle for more details! ...

Determine the overall sum of rows present within this particular tbody section

I am struggling to calculate the total number of tr's within my nested tbody, but I am not getting the correct count. The jQuery code I used is returning a high number like 44 rows instead of the expected 7 rows. Can anyone point out where I might ha ...

The Angular service encounters issues when interacting with REST API

Within my application, a template is utilized: <div class="skills-filter-input" ng-class="{'hidden-xs': skillsFilterHidden}"> <input type="text" ng-model="skillQuery" ng-change="filterSkills()" placeholder="Filter skills" class="filter- ...

The 'required' validator in Mongoose seems to be malfunctioning

I've been attempting to validate the request body against a Mongoose model that has 'required' validators, but I haven't been successful in achieving the desired outcome so far. My setup involves using Next.js API routes connected to Mo ...

What could be the reason for the Mongoose findAll function causing a 500 error to occur?

My model / Schema has a working create method, but the "all" method is causing a 500 error. var mongoose = require('mongoose'); var Schema = mongoose.Schema; var DiSchema = new mongoose.Schema({ name: { type: String, lowercase: true , require ...

What could be causing the styled-component's flex value to not update?

I have a sidebar and main content on my website layout. The main content occupies most of the screen space, while the sidebar should only take up a small portion. Both elements are within a flexbox container, with the sidebar and main content as child divs ...

Having trouble retrieving client data on the server-side using Ionic and Express.js

I have a question regarding my Ionic project setup. I have a Node.js and express.js project running on localhost to handle my http requests. When I send data from the client-side to the server-side, the data received looks like this when I log it: { &apos ...

To avoid the browser context menu from appearing when interacting with a d3.js element, simply employ a different way of clicking

Currently, I have a d3.js element plotted in the form of a rectangle: // draw rectangle svg.selectAll(".rect").append("rect") .attr("y", 10) .attr("x", 10) .attr("height", 5) .attr("width", 5) .on("contextmenu", function (d, i) { // ...

Trigger useEffect after prop has been changed

I'm trying to figure out how I can avoid running an API call in my component on initial rendering. The prop needed for the API call should be updated only after a form submission. Although I have included the prop in the dependency array of useEffect, ...

custom vertical tab label text not aligning to the right in material-ui with textAlign

I am struggling with customizing the tabs in material-ui to display them vertically. I also want the text labels of these tabs to align to the right for a more cohesive look. Despite trying various styling options, I have not been successful in achieving t ...

What is the best approach: Referencing schema within properties or including it as an array in Mongoose?

Consider the scenario where we have two schemas, User and Post. Should we include a reference to User in Post's properties or should we add an array of Post schema inside User schema? Which approach is more efficient in terms of performance and other ...

Angular 10 introduces a new feature where BehaviorSubject can now hold and emit two

Currently, I am attempting to log in using Firebase. The login system is functioning correctly; however, I am facing a challenge in retrieving the error name from the authentication service and displaying it in my login component. SignIn(email: string, pas ...

What is the best way to convert base64 into an image using Node.js?

I'm having trouble with decoding an image sent as base64 through sockets. The file that should contain the image is being written as a base64 string instead of a jpg file. For encoding through socket: function encode_base64(filename) { fs.readFile( ...

Encountering the error "ReferenceError: Cannot access 'data' before initialization" during deployment on Vercel

I encountered a problem with my project after trying to implement dynamic routing. Initially, everything was working fine locally and during deployment. However, when I attempted to incorporate dynamic routing, errors started to occur. Unfortunately, I am ...

Placeholder variable not specified in radio buttons

I am currently facing challenges applying validations to radio buttons in Angular. Normally, I create a #templateRefVariable on the input for other input types, which allows me to access the NgControl and use properties like touched. My goal is to set the ...