How do i make a function that can transform data of type DevicesType to GroupsType bellow?
So instead of getting list of devices saying what groups they are in I need a list of groups containing 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"
}[]
}[]
Example data would be:
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 wanna do this to render tables for each group with list of their respective devices.
Presented below is one possible implementation (in JavaScript) to achieve the target:
const devicesFromBackend = [
{
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 getGroups = (arr = devicesFromBackend) => (
Object.values( // extract only the values of the object generated
arr.reduce( // iterate through the devices array using reduce
(fin, {groups, ...rest}) => ({ // fin is the object generated (aggregated)
...fin, // they key (used in fin object) will be the group-id
...groups.reduce( // now iterate through the groups array
(acc, {id, name}) => ({ // acc is the accumulator
...acc, // de-structured the group's name, id in above line
[id]: id in fin // if the group-id already exists in fin-object
? { // populate the existing group details as-is
...fin[id], // then, add the new device into the existing group
devices: fin[id].devices.concat([{
...rest
}])} // if group-id is not already present in fin, add it now
: {id, name, devices: [{...rest}]}
}),
{}
)
}),
{}
)
)
);
console.log(getGroups());
Approach is explained within the code, as comments. If required, these can be listed separately in this answer.
Here's a possible solution that works with 0 dependencies
function convertDevicesToGroups(devices: DevicesType): GroupsType {
// Isolate groups
const groups = devices.map((d) => d.groups). // Extract groups
reduce((g, i = []) => i.concat(...g)). // Convert from 2D to 1D
filter((g, i, self) => i === self.findIndex((e) => e.id === g.id)); // Remove duplicates
return groups.map((group) => {
const belongDevices = devices.
filter((d) => d.groups.find((g) => g.id === group.id)). // Filter devices that belog to a group
map((d) => ({ id: d.id, name: d.name, status: d.status })); // Extract required property
return {
...group,
devices: belongDevices,
};
});
}
Jump to the playground for complete code and example.
As well, I would recommend small improvements on your type for 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[]
The best things would be to remove that implicit circular dependency, but it requires more refactor that is not related to your current problem