I have currently done with my code transformation on my api data by its roleId. However, I need to display another view which will group the data according to which projectId the users are in.
I can simply copy and paste and create another one for projectIds data transformation, however, I feel that my approach may be messy and not easily reusable. So I would like to ask if there is a better way to do this?
Because i cannot simply swap roleIds in the function to projectIds by putting rolesId or projectIds in a variable to be reused in the function.
Can anyone help me please ?
Code for the api data transformation to dislay in ant design tree data table:
let apiData = [
{
email: "alyssayo@xxx.com",
permissionIds: null,
roleIds: ["raa", "baa", "caa"],
projectIds: ["1aa", "3aa"]
},
{
email: "chiuewww@xxx.com",
permissionIds: null,
roleIds: ["baa", "caa"],
projectIds: ["1aa", "2aa", "3aa"]
},
{
email: "lalaqq@xxx.com",
permissionIds: null,
roleIds: ["caa"],
projectIds: ["1aa"]
},
{
email: "sqssq@xxx.com",
permissionIds: null,
roleIds: [],
projectIds: []
}
];
//Isolate and transform data by roleId
const transData = apiData.reduce((arr, item) => {
let formatted = item.roleIds.map((id) => {
return {
roleIds: id,
children: [{ ...item, roleIds: id }]
};
});
return [...arr, ...formatted];
}, []);
//Group transformed data by roleIds
const findMatch = (arr, roleIds) =>
arr.find((item) => item.roleIds === roleIds);
const groupArray = (originalArr) => {
return Array.isArray(originalArr)
? originalArr.reduce((previousObj, obj) => {
if (findMatch(previousObj, obj.roleIds)) {
findMatch(previousObj, obj.roleIds).children.push(...obj.children);
} else {
previousObj.push(obj);
}
return previousObj;
}, [])
: "Need an array";
};
//Call the group roleId function on transformed data by roleId
const userRoledata = groupArray(transData);
//Add key to parent and children
let key = 1;
userRoledata.forEach((item) => {
item.key = key++;
item.children.forEach((child) => {
child.key = key++;
});
});
setData(userRoledata); //this will be dataSource for table rendering in ant design
What will the data transformed display when used as dataSource in ant design:
If grouped by roleIds:
[
{
"roleIds": "raa",
"children": [
{
"email": "alyssayo@xxx.com",
"permissionIds": null,
"roleIds": "raa",
"projectIds": [
"1aa",
"3aa"
],
"key": 2
}
],
"key": 1
},
{
"roleIds": "baa",
"children": [
{
"email": "alyssayo@xxx.com",
"permissionIds": null,
"roleIds": "baa",
"projectIds": [
"1aa",
"3aa"
],
"key": 4
},
{
"email": "chiuewww@xxx.com",
"permissionIds": null,
"roleIds": "baa",
"projectIds": [
"1aa",
"2aa",
"3aa"
],
"key": 5
}
],
"key": 3
},
{
"roleIds": "caa",
"children": [
{
"email": "alyssayo@xxx.com",
"permissionIds": null,
"roleIds": "caa",
"projectIds": [
"1aa",
"3aa"
],
"key": 7
},
{
"email": "chiuewww@xxx.com",
"permissionIds": null,
"roleIds": "caa",
"projectIds": [
"1aa",
"2aa",
"3aa"
],
"key": 8
},
{
"email": "lalaqq@xxx.com",
"permissionIds": null,
"roleIds": "caa",
"projectIds": [
"1aa"
],
"key": 9
}
],
"key": 6
}
]
If grouped by projectIds:
[
{
"projectIds": "1aa",
"children": [
{
"email": "alyssayo@xxx.com",
"permissionIds": null,
"roleIds": [
"raa",
"baa",
"caa"
],
"projectIds": "1aa",
"key": 2
},
{
"email": "chiuewww@xxx.com",
"permissionIds": null,
"roleIds": [
"baa",
"caa"
],
"projectIds": "1aa",
"key": 3
},
{
"email": "lalaqq@xxx.com",
"permissionIds": null,
"roleIds": [
"caa"
],
"projectIds": "1aa",
"key": 4
}
],
"key": 1
},
{
"projectIds": "3aa",
"children": [
{
"email": "alyssayo@xxx.com",
"permissionIds": null,
"roleIds": [
"raa",
"baa",
"caa"
],
"projectIds": "3aa",
"key": 6
},
{
"email": "chiuewww@xxx.com",
"permissionIds": null,
"roleIds": [
"baa",
"caa"
],
"projectIds": "3aa",
"key": 7
}
],
"key": 5
},
{
"projectIds": "2aa",
"children": [
{
"email": "chiuewww@xxx.com",
"permissionIds": null,
"roleIds": [
"baa",
"caa"
],
"projectIds": "2aa",
"key": 9
}
],
"key": 8
}
]
Define a transform
function with 2 parameter. First the apiData
, which is the data you want to transform and secondly the transformation_key
which is a string of either roleIds
or projectIds
.
Within this function you first to generate an object with the different roleIds/projectIds as keys and for each key an array of all the items included.
To do so you make use of a reducer and loop over the items
apiData.reduce((obj, item) => {
if (!item[transformation_key]) return obj; // in case item[transformation_key] is null, you can skip the item an just return the item as is.
... // if not, we've to reduce over the array of roleIds/projectIds within the item as well.
}, {}) // {} is the new object (`obj` refers to this within the reducer)
Within each item we also have to loop over all the items in the roleIds/projectIds of that item, so we add a second/inner reducer.
// item[transformation_key] is the array of roleIds/projectIds within your item.
item[transformation_key].reduce((cur, id) => {
// `cur` is actually the same object as the `obj` from the outer reducer.
if (!cur[id]) cur[id] = [] // if the key/id doesn't excist yet on the object, we set it equal to an empty array.
cur[id].push({
...item,
[transformation_key]: id
}) // we push the item to the array (using the spread operator and updating the value for the `transformation_key` within the item.
return cur // you must return the object `cur`.
}, obj) // we pass to `obj` from the outer reducer into the inner reducer.
This will generate an object like
const transformedObject = {
[roleIds/projectIds] : [
... all the children
]
}
next we map the ids to the required output
return Object.keys(transformedObject).map(key => {
return {
[transformation_key]: key,
children: transformedObject[key]
}
})
To summarize
function transform(apiData, transformation_key) {
if (!(transformation_key == 'roleIds' || transformation_key == 'projectIds')) throw new Error("Choose either 'roleIds' or 'projectIds' as a transformation_key")
const transformedObject = apiData
.reduce((obj, item) => {
if (!item[transformation_key]) return obj;
return item[transformation_key].reduce((cur, id) => {
if (!cur[id]) cur[id] = []
cur[id].push({
...item,
[transformation_key]: id
})
return cur
}, obj)
}, {});
return Object.keys(transformedObject).map(key => {
return {
[transformation_key]: key,
children: transformedObject[key]
}
})
}
const transDataByRoleIds = transform(res.data, 'roleIds')
const transDataByProjectIds = transform(res.data, 'projectIds')