Nested Array Of Objects into Downloadable CSV in react - javascript

I have the following Data which when downloaded can be viewed in an xls/csv format. I am using react-csv npm package which is displaying the name and description in same columns as opposed to different columns.
I need some help in figuring out how to display data where Name, Description, and Roles are displayed in different columns as shown below.
Data:-
const csvData = [
{
name: 'Data science training',
description:
'Data Science certification training',
suggestedRoles: [
{ id: 16, category: 'DEVELOPER', name: 'Data Engineer' },
{ id: 17, category: 'DEVELOPER', name: 'Data Scientist' }]
},{
name: 'AWS',
description:
'AWS certification training',
suggestedRoles: [
{ id: 16, category: 'DEVELOPER', name: 'Cloud Engineer' },
{ id: 17, category: 'DEVELOPER', name: 'Network Engineer' }]
}],
Expected Output:-
Name Description RoleName
Data Science Training Data Science Certification Training Data Engineer
Data Scientist
AWS Training AWS Certification Training Cloud Engineer
Network Engineer
Current Output:-
Name Description RoleName
Data Science Training,AWS Training Data Science Certification Training, AWS Certification Training Data Engineer,Data Scientist,Cloud Engineer,Network Engineer
Code:-
export const ReactCsv = () => {
const createCsvFileName = ()=> `data_${moment().format()}.csv`;
const headers = [
{ label: 'Name', key: 'name' },
{ label: 'Description', key: 'description' },
{ label: 'SuggestedRoles', key: 'suggestedRoles' }
];
const data = [
{
name: csvData.map((_)=>_.name),
description: csvData.map((_)=>_.description),
suggestedRoles: csvData.map((_)=>_.suggestedRoles.map((role)=>role.name)),
}
];
return (
<CSVLink
data={data}
headers={headers}
filename={createCsvFileName()}
target="_blank"
style={{ textDecoration: 'none', outline: 'none', height: '5vh' }}
>
<Button variant="contained" color="secondary" style={{ height: '100%' }}>
Download CSV
</Button>
</CSVLink>
);
};

The data variable must be an array of objects, not an array of a single object where each property is an array of values.
const csvData = [{
name: 'Data science training',
description: 'Data Science certification training',
suggestedRoles: [{
id: 16,
category: 'DEVELOPER',
name: 'Data Engineer'
},
{
id: 17,
category: 'DEVELOPER',
name: 'Data Scientist'
}
]
}, {
name: 'AWS',
description: 'AWS certification training',
suggestedRoles: [{
id: 16,
category: 'DEVELOPER',
name: 'Cloud Engineer'
},
{
id: 17,
category: 'DEVELOPER',
name: 'Network Engineer'
}
]
}];
const data = csvData.map(item => ({
name: item.name,
description: item.description,
suggestedRoles: item.suggestedRoles.map(role => role.name),
}))
console.log(data);

Your ReactCsv's data generating code should be changed into like this one.
export const ReactCsv = () => {
const createCsvFileName = ()=> `data_${moment().format()}.csv`;
const headers = [
{ label: 'Name', key: 'name' },
{ label: 'Description', key: 'description' },
{ label: 'SuggestedRoles', key: 'suggestedRoles' }
];
let data = []
csvData.forEach(item => {
data.push({
name: item.name,
description: item.description,
suggestedRoles: item.suggestedRoles[0].name
});
for (let i = 1; i < item.suggestedRoles.length; i++) {
const role = item.suggestedRoles[i];
data.push({
name: '',
description: '',
suggestedRoles: role.name
});
}
});
return (
<CSVLink
data={data}
headers={headers}
filename={createCsvFileName()}
target="_blank"
style={{ textDecoration: 'none', outline: 'none', height: '5vh' }}
>
<Button variant="contained" color="secondary" style={{ height: '100%' }}>
Download CSV
</Button>
</CSVLink>
);
};

The data property should be one single object. So, what you can do is create a function that returns an object. That object structure you can decide based on the data and attributes you want to display.
eg:
const newBillingReport = () => {
try {
const newBillingReportData = billingExportReportData.map((data) => ({
"Type": data?.type || "Not Provided",
"Status": data?.status || "Not Provided",
"Billed": data?.billedAmount || "0",
}),[])
return newBillingReportData
} catch (error) {
console.error(error)
}
}
This will returns object with three attributes:
eg:
{
"Type": "COMPLETED",
"Status": "DONE",
"Billed": "True"
}
So like this you can create a function. Then for the header attribute map same as you returned. That should also will be a function that returns an object
eg:
const formatUserName = () => {
return [
{ label: "Type", key: "Type" },
{ label: "Status", key: "Status" },
{ label: "Billed ($)", key: "Billed" },
]
}
In here you can see how you need to add label and keys. Most likely the function return data.
So CVLink like this
headers={headers}
data={newBillingReport()}
headers should call the function const headers = formatUserName()

Related

Display SectionList from API data in react native

I'm trying to create a SectionList from data received from an API. This API has the following structure for the ingredients that I want to display:
const ingredients = {
malt: [
{
name: 'Maris Otter Extra Pale',
amount: {
value: 3.3,
unit: 'kilograms',
},
},
{
name: 'Caramalt',
amount: {
value: 0.2,
unit: 'kilograms',
},
},
{
name: 'Munich',
amount: {
value: 0.4,
unit: 'kilograms',
},
},
],
hops: [
{
name: 'Fuggles',
amount: {
value: 25,
unit: 'grams',
},
add: 'start',
attribute: 'bitter',
},
{
name: 'First Gold',
amount: {
value: 25,
unit: 'grams',
},
add: 'start',
attribute: 'bitter',
},
{
name: 'Fuggles',
amount: {
value: 37.5,
unit: 'grams',
},
add: 'middle',
attribute: 'flavour',
},
{
name: 'First Gold',
amount: {
value: 37.5,
unit: 'grams',
},
add: 'middle',
attribute: 'flavour',
},
{
name: 'Cascade',
amount: {
value: 37.5,
unit: 'grams',
},
add: 'end',
attribute: 'flavour',
},
],
yeast: 'Wyeast 1056 - American Ale™',
};
The desired result is the following using SectionList (with malt and hops as section headers):
Visual example of result
I've already tried to use functions like Object.values(), with no result at all. The code simply looks like the following, receiving the beer from the previous view(with a beer list):
const Detail = ({ route }) => {
const beer = route.params.beer;
const ingredientsFormatted = Object.values(beer.ingredients);
return(
<SectionList
sections={ingredientsFormatted}
renderItem={({ item }) => {
<Text>{item.name}</Text>; //Here there has to be the name of the different ingredients
}}
renderSectionHeader={({ section }) => <Text>{section}</Text>} //Here there has to be malt, hops or yeast
keyExtractor={(item) => item.name}
></SectionList>
)}
There are 2 problems in your ingredients data.
Format in value for yeast is not the same as the others.
Your data inserted into SectionList is not the format recommended by React-Native Offical docs.
To fix this, you can modify your API return / map the data after retrieved from server.
const Detail = ({ route }) => {
const beer = route.params.beer;
let sectionListTemplate = [];
for (let [key, value] of Object.entries(beer.ingredients)) {
//console.log(`${key}: ${JSON.stringify(value)}`);
if(!Array.isArray(value)){
//Handle case for 'yeast' which its data is not an array
sectionListTemplate.push({
title: key,
data: [{
name: value
}]
})
}
else{
//Map value for general cases
sectionListTemplate.push({
title: key,
data: value
})
}
}
return(
<SectionList
sections={sectionListTemplate}
renderSectionHeader={({ section }) => {
return(
//Here there has to be malt, hops or yeast
<Text style={{fontWeight: 'bold'}}>
{section.title}
</Text>
)
}}
renderItem={({ item }) => {
return(
//Here there has to be the name of the different ingredients
<Text>
{item.name}
</Text>
)
}}
keyExtractor={(item) => item.name}
>
</SectionList>
)
)}

React JS / ANTD: Create new object entries from exisiting object array

I am currently working with ANTD and React v18.
I want to display my data inside the table. For this, I need to create objects with regarding index and values.
The logic for a table is like this:
const dataSource = [
{
key: '1',
name: 'Mike',
age: 32,
address: '10 Downing Street',
},
];
const columns = [
{
title: 'Name',
dataIndex: 'name',
key: 'name',
},
{
title: 'Age',
dataIndex: 'age',
key: 'age',
},
{
title: 'Address',
dataIndex: 'address',
key: 'address',
},
];
<Table dataSource={dataSource} columns={columns} />;
Now I have an object array like this:
const personData = [
{
"_id": "C84113",
"Firstname": "Hola",
"Lastname": "Tester",
"__typename": "Person"
},
{
"_id": "C_C84113_0",
"Firstname": "Hermanoi",
"Lastname": "Oko",
"__typename": "Person"
}
]
How can I map or push these values, that I receive something like this ?
//we create the respective columns
const personTableColumn = [
{ title: 'Person (Teilakte)', dataIndex: 'personName' },
{ title: 'ID', dataIndex: 'personID' },
];
//I want to have it like this
//personData.foreach() or push()
{
"key":"i",
"personName":"Person.Firstname +" "+ Person.Lastname",
"personID":"Person.ID>"
}
You can iterate over the array and use the map function to create a new array with the object you need.
const personDataSource = personData.map((person, i) => {
return {
key: i,
personName: person.Firstname + " " + person.Lastname,
personID: person._id
};
});
The personDataSource will have JSON object:
[
{ key: 0, personName: "Hola Tester", personID: "C84113" },
{ key: 1, personName: "Hermanoi Oko", personID: "C_C84113_0" }
]

How to re-map an array to use in Ant Design Tree

I have this code structure:
const data = [
{
name: 'Lebron',
sports: 'Basketball',
},
{
name: 'Durant',
sports: 'Basketball',
},
{
name: 'Federrer',
sports: 'Tennis',
},
{
name: 'Nadal',
sports: 'Tennis',
},
];
and I'm trying to transform it into this:
const treeData = [
{
title: 'Select All',
key: 'All',
children: [
{
title: 'Basketball',
key: 'Basketball',
children: [
{
title: 'Lebron',
key: 'Lebron',
},
{
title: 'Durant',
key: 'Durant',
},
],
},
{
title: 'Tennis',
key: 'Tennis',
children: [
{
title: 'Federrer',
key: 'Federrer',
},
{
title: 'Nadal',
key: 'Nadal',
},
],
},
],
},
];
to use in Ant Design Tree link.
Right now my plan is to get all the sports like this:
let sports = data.map(({ sports }) => sports);
sports = [...new Set(sports)];
but after that I have no idea on what should be my next step to achieve the treeData
You can use .reduce() to accumulate all sports to a Map, where each sport is a key and each value for the sport is an array of associated names. Once you have built the Map, you can use Array.from() to convert your map into your desired children array. This can be done by providing a mapping function as the second argument to Array.from(), and using it to convert each entry ([key, value]) pair to an object of your desired structure.
See example below:
const data = [ { name: 'Lebron', sports: 'Basketball', }, { name: 'Durant', sports: 'Basketball', }, { name: 'Federrer', sports: 'Tennis', }, { name: 'Nadal', sports: 'Tennis', }, ];
const children = Array.from(data.reduce((m, {name, sports}) =>
m.set(sports, [...(m.get(sports) || []), name])
, new Map), ([key, arr]) => ({title: key, key, children: arr.map(title => ({title, key: title}))}));
const treeData = [{title: 'Select All', key: 'All', children}];
console.log(treeData);
const data = [
{
name: 'Lebron',
sports: 'Basketball',
},
{
name: 'Durant',
sports: 'Basketball',
},
{
name: 'Federrer',
sports: 'Tennis',
},
{
name: 'Nadal',
sports: 'Tennis',
},
];
const dataMap: Record<string, string[]> = {};
data.forEach(({ name, sports }) => {
dataMap[sports] ??= []
dataMap[sports].push(name)
})
const treeData = [{
title: 'Select All',
key: 'All',
children: Object.entries(dataMap).map(([sport, names]) => ({
title: sport,
key: sport,
children: names.map(name => ({
title: name,
key: name
}))
}))
}]
Here's a two-steps way of get the children of your final object:
First, use a reduce to create an object of sports/children, where children matches what you have in your final output:
const middle = data.reduce((transformed, item) => {
if (!transformed[item.sports]) {
transformed[item.sports] = [];
}
transformed[item.sports].push({
title: item.name,
key: item.name
});
return transformed;
}, {});
Then map the results of the reduce function to reshape the object:
const results = Object.entries(middle).map(([key, children]) => ({
title: key,
key,
children
}));

How to use JavaScript to turn csv file with hierarchy relationships to an object?

I have an csv file that contains an organization hierarchy data, one of the fields is the id of that employee's subordinates' ids.
The csv file is something looking like this:
id,name,title,children
n1,Lao Lao,general manager,"n2,n3,n9,n10"
n2,Bo Miao,department manager
n3,Su Miao,department manager,"n4,n5,n8"
n4,Tie Hua,senior engineer
n5,Hei Hei,senior engineer,"n6,n7"
n6,Dan Dan,engineer
n7,Xiang Xiang,engineer
n8,Pang Pang,senior engineer
n9,Hong Miao,department manager
n10,Chun Miao,department manager,"n11"
n11,Yue Yue,senior engineer
The end result I am looking for is something like this:
{
id: "n1",
name: "Lao Lao",
title: "general manager",
children: [
{ id: "n2", name: "Bo Miao", title: "department manager" },
{
id: "n3",
name: "Su Miao",
title: "department manager",
children: [
{ id: "n4", name: "Tie Hua", title: "senior engineer" },
{
id: "n5",
name: "Hei Hei",
title: "senior engineer",
children: [
{ id: "n6", name: "Dan Dan", title: "engineer" },
{ id: "n7", name: "Xiang Xiang", title: "engineer" },
],
},
{ id: "n8", name: "Pang Pang", title: "senior engineer" },
],
},
{ id: "n9", name: "Hong Miao", title: "department manager" },
{
id: "n10",
name: "Chun Miao",
title: "department manager",
children: [{ id: "n11", name: "Yue Yue", title: "senior engineer" }],
},
],
}
What i've tried so far:
So far I've used csvtojson package to parse the csv file into an array, which gives me this:
[
{
id: 'n1',
name: 'Lao Lao',
title: 'general manager',
children: 'n2,n3,n9,n10'
},
{ id: 'n2', name: 'Bo Miao', title: 'department manager' },
{
id: 'n3',
name: 'Su Miao',
title: 'department manager',
children: 'n4,n5,n8'
},
{ id: 'n4', name: 'Tie Hua', title: 'senior engineer' },
{
id: 'n5',
name: 'Hei Hei',
title: 'senior engineer',
children: 'n6,n7'
},
{ id: 'n6', name: 'Dan Dan', title: 'engineer' },
{ id: 'n7', name: 'Xiang Xiang', title: 'engineer' },
{ id: 'n8', name: 'Pang Pang', title: 'senior engineer' },
{ id: 'n9', name: 'Hong Miao', title: 'department manager' },
{
id: 'n10',
name: 'Chun Miao',
title: 'department manager',
children: 'n11'
},
{ id: 'n11', name: 'Yue Yue', title: 'senior engineer' }
]
I think some recursion is required to transform this data, but I am not super good with that, any help is appreciated!
Edit: Also I am flexible on how the columns look in csv, if there is anyway to make this transformation easier by alter the columns that is cool as well.
Edit: I am playing with the idea of having a report to field in the csv, I think this'll make it much easier;
id,name,title,report_to
n1,Lao Lao,general manager
n2,Bo Miao,department manager,n1
n3,Su Miao,department manager,n1,
n4,Tie Hua,senior engineer,n3
n5,Hei Hei,senior engineer,n3,
n6,Dan Dan,engineer,n5
n7,Xiang Xiang,engineer,n5
n8,Pang Pang,senior engineer,n3
n9,Hong Miao,department manager,n1
n10,Chun Miao,department manager,n1
n11,Yue Yue,senior engineer,n10
Edit: after I change the csv columns I was able to come up with a solution. Gonna post it below in case other people are looking for this in the future.
const source = await CSVToJSON().fromFile("./example.csv");
const dataForOrgChart = source
.map((employee) => {
employee.children = source.filter(
(child) => child.report_to === employee.id
);
return employee;
})
.map((employee) => {
delete employee.report_to;
return employee;
})
.filter(
(employee) =>
!source
.map((employee) => employee.children.map((child) => child.id))
.reduce((acc, cur) => [...acc, ...cur], [])
.includes(employee.id)
)[0];
console.log(JSON.stringify(dataForOrgChart));
I am not even 100% sure how i got here, its just tons of trials.
It seems to be working but if someone notice any bugs please point out. Also thanks a lot for the different approaches. I am going to learn them all! I am a newbie on stackoverflow and this is a warm welcome!
I created the following code snippet with the help of this link,
this solution may help you.
// csv data parsing function from
// https://stackoverflow.com/questions/1293147/javascript-code-to-parse-csv-data
var parseCSV2 = function(s, sep) {
// http://stackoverflow.com/questions/1155678/javascript-string-newline-character
var universalNewline = /\r\n|\r|\n/g;
var a = s.split(universalNewline);
for (var i in a) {
for (var f = a[i].split(sep = sep || ","), x = f.length - 1, tl; x >= 0; x--) {
if (f[x].replace(/"\s+$/, '"').charAt(f[x].length - 1) == '"') {
if ((tl = f[x].replace(/^\s+"/, '"')).length > 1 && tl.charAt(0) == '"') {
f[x] = f[x].replace(/^\s*"|"\s*$/g, '').replace(/""/g, '"');
} else if (x) {
f.splice(x - 1, 2, [f[x - 1], f[x]].join(sep));
} else f = f.shift().split(sep).concat(f);
} else f[x].replace(/""/g, '"');
}
a[i] = f;
}
return a;
}
//the CSV data
var csvData = `id,name,title,children
n1,Lao Lao,general manager,"n2,n3,n9,n10"
n2,Bo Miao,department manager
n3,Su Miao,department manager,"n4,n5,n8"
n4,Tie Hua,senior engineer
n5,Hei Hei,senior engineer,"n6,n7"
n6,Dan Dan,engineer
n7,Xiang Xiang,engineer
n8,Pang Pang,senior engineer
n9,Hong Miao,department manager
n10,Chun Miao,department manager,"n11"
n11,Yue Yue,senior engineer`;
//add nested employeess
function addtoDepartment(employeesArray, employee) {
if (!employee.addedToDepartement) {
let departmentEmp = {
id: employee.id,
name: employee.name,
title: employee.title
};
if (employee.childrenId) {
employee.childrenId.split(",").forEach(id => {
let emp = employeesArray.find(e => e.id == id);
let dep = addtoDepartment(employeesArray, emp);
if (dep) {
if (!departmentEmp.children)
departmentEmp.children = [];
departmentEmp.children.push(dep)
}
})
}
employee.addedToDepartement = true;
return departmentEmp;
}
return false;
}
let employeParsed = parseCSV2(csvData, ",");
let employees = [];
//add CSV emplyee data as employee object to an array
for (let i = 1; i < employeParsed.length; i++) {
empData = employeParsed[i];
employees.push({
id: empData[0] || '',
name: empData[1] || '',
title: empData[2] || '',
childrenId: empData[3],
addedToDepartement: false
});
}
//add employees to departments or under supervisor
var department = [];
for (let i = 0; i < employees.length; i++) {
let depEmp = addtoDepartment(employees, employees[i]);
if (depEmp)
department.push(depEmp);
}
//FINAL output
console.log(department);
If the ids are all different, you could convert the csv into a nice map:
You could try doing:
let parsed = [
{
id: 'n1',
name: 'Lao Lao',
title: 'general manager',
children: 'n2,n3,n9,n10'
},
{ id: 'n2', name: 'Bo Miao', title: 'department manager' },
{
id: 'n3',
name: 'Su Miao',
title: 'department manager',
children: 'n4,n5,n8'
},
{ id: 'n4', name: 'Tie Hua', title: 'senior engineer' },
{
id: 'n5',
name: 'Hei Hei',
title: 'senior engineer',
children: 'n6,n7'
},
{ id: 'n6', name: 'Dan Dan', title: 'engineer' },
{ id: 'n7', name: 'Xiang Xiang', title: 'engineer' },
{ id: 'n8', name: 'Pang Pang', title: 'senior engineer' },
{ id: 'n9', name: 'Hong Miao', title: 'department manager' },
{
id: 'n10',
name: 'Chun Miao',
title: 'department manager',
children: 'n11'
},
{ id: 'n11', name: 'Yue Yue', title: 'senior engineer' }
];
let mapping = {};
parsed.forEach(v => mapping[v.id] = v); // Maps out employees
function organize(key) {
if (!mapping[key]) return;
if (typeof mapping[key].children === 'string') {
let children = {};
mapping[key].children.split(',').forEach(child => {
child = mapping[child];
children[child.id] = organize(child.id);
delete mapping[child.id];
});
mapping[key].children = children;
}
return mapping[key];
}
Object.keys(mapping).forEach(organize);
console.log(mapping);

How to get and link nested value to field passing columns to Material Table

I'm trying to create a table and I have nested data like this:
[{
id: 24,
name: "DANIEL TSUTOMU OBARA",
number: "134",
phone: "11111111111",
rg: "4034092666",
EmployeeStatus: {
createdAt: "2019-08-07T14:38:52.576Z"
description: "Documentos rejeitados"
id: 3
updatedAt: "2019-08-07T14:38:52.576Z"
},
Sector: {
id: 2,
name: "Controladoria"
}
}]
And have this structure columns table:
columns: [
{ title: "Nome", field: "name" },
{ title: "CPF", field: "cpf" },
{ title: "Função", field: "FunctionId", lookup: {} },
{
title: "Setor",
field: "SectorId",
lookup: {}
},
{
title: "Status",
field: "EmployeeStatus", <------- I want to get description here
editable: "never"
}
],
Then, I need to pass these columns into my Material-table like this:
<MaterialTable
columns={state.columns}
data={state.data}
title=""
icons={tableIcons}
editable={{
onRowAdd: newData => createInstructor(newData),
onRowUpdate: async (newData, oldData) =>
updateInstructor(newData, oldData),
onRowDelete: oldData => deleteInstructor(oldData)
}}
/>
So, how I can do that access nested data into column field?
Thanks!
Please find the below solution.
I am expecting the data to have other objects too, so finding the first object with the key available.
let data = [{
id: 24,
name: "DANIEL TSUTOMU OBARA",
number: "134",
phone: "11111111111",
rg: "4034092666",
EmployeeStatus: {
createdAt: "2019-08-07T14:38:52.576Z",
description: "Documentos rejeitados",
id: 3,
updatedAt: "2019-08-07T14:38:52.576Z"
},
Sector: {
id: 2,
name: "Controladoria"
}
}]
let columns = [
{ title: "Nome", field: "name" },
{ title: "CPF", field: "cpf" },
{ title: "Função", field: "FunctionId", lookup: {} },
{
title: "Setor",
field: "SectorId",
lookup: {}
},
{
title: "Status",
field: "EmployeeStatus",
editable: "never"
}
];
let columnToUpdate = columns.find(obj => obj.title==="Status"); //find the column in columns array
let desc = data.find(obj => Object.keys(obj).includes('EmployeeStatus')).EmployeeStatus.description; // get description
columnToUpdate.field = desc; // mutate the object
console.log(columns);
I just decided to use lookup functionality and solve in this way for now.
const [state, setState] = useState({
columns: [
{ title: "ID", field: "id", editable: "never" },
{ title: "Nome", field: "name" },
{ title: "CPF", field: "cpf" },
{ title: "Função", field: "FunctionId", lookup: {} }, // 3
{
title: "Setor",
field: "SectorId",
lookup: {}
}, // 4
{
title: "Status",
field: "EmployeeStatusId",
lookup: {},
editable: "never"
}, // 5
]});
....
await api
.get(`/employee-status`)
.then(result => {
status = formatSectors(state, result, 5);
})
.....
const formatSectors = (state, result, position) => {
let columns = state.columns;
const indexColumnSector = 4;
columns[position].lookup = result.data.rows.reduce(
(accumulator, currentValue) => {
accumulator[currentValue.id] =
position === indexColumnSector
? currentValue.name
: currentValue.description;
return accumulator;
},
{}
);
return columns;
};

Categories

Resources