React Table - Show nested object fields - javascript

Hey so I cannot figure this out for the life of me. SOS.
I have a JSON with nested objects that looks like this:
[
{
"id": 1,
"internalToolName": "HP ALM",
"statuses": [
{
"id": 57,
"statusType": "Ok",
"date": "2018-12-17"
},
{
"id": 67,
"statusType": "Ok",
"date": "2018-12-23"
},
{
"id": 37,
"statusType": "Ok",
"date": "2018-01-06"
}
]
},
{
"id": 2,
"internalToolName": "Artifactory",
"appType": null,
"statuses": [
{
"id": 1652,
"statusType": "Ok",
"date": "2018-12-31"
},
{
"id": 119,
"statusType": "Ok",
"date": "2018-12-28"
}
]
}
]
I'm trying to populate a react-table like this
I was able to populate the tool name rows using the 'Internal Tools' header and populated the days of the current month. I'm having trouble figuring out the logic to populate the cells with the status on the given date field of the status object. Here is my code:
export default class ToolsIssue2 extends Component {
constructor(props) {
super(props);
this.state = {
month: moment().format('MMMM, YYYY'),
internalToolData: [{
internalToolName: '',
statuses: [{
date: '',
statusType: '',
}]
}],
}
}
componentDidMount() {
this.generateData();
}
generateData() {
axios.get('http://localhost:8080/internal_tool')
.then(response => {
this.setState({
internalToolData: response.data
});
console.log(this.state.internalToolData);
}).catch(error => alert(error));
}
setStatusSVG(statusType) {
switch (statusType) {
case 'Ok': return <img src={ok} className='OK' alt='ok-logo' />
case 'Warning': return <img src={warning} className='Warning' alt='warning-logo' />
case 'Unavailable': return <img src={unavailable} className='Unavailable' alt='unavailable-logo' />
}
}
getMonth() {
return moment().format('MMMM, YYYY');
}
getCurrentColumns() {
var daysInMonth = moment().daysInMonth();
var monthDaysColumn = [];
for (var i = 1; i <= daysInMonth; i++) {
monthDaysColumn.push(
{
// id: 'statusDate',
Header: i,
accessor: 'statuses',
Cell: (row) => {
if(moment(`${row.row.statuses.date}`).year() === moment().year() && moment(`${row.row.statuses.date}`).date() === i) {
this.setStatusSVG(row.row.statuses.statusType);
}
},
resizable: false,
sortable: false,
width: 35,
})
}
return monthDaysColumn;
}
render() {
const { internalToolData } = this.state;
return (
<div>
<ReactTable
data={internalToolData}
columns={[{
Header: 'Internal Tools',
columns: [{
accessor: 'internalToolName',
width: 150,
resizable: false,
}]
}, {
Header: this.getMonth(),
columns: this.getCurrentColumns(),
}
]}
defaultPageSize={15}
className="-striped -highlight"
/>
<br />
</div>
)
}
}

Related

How to iterate over a object that is returned asynchronously and display it in react.js?

Im having trouble with displaying the results of the function call await toxic("a toxic sentence"). The closest i could get was to see this update in the state of the component in react plugin however it did not update the component itself.
function ToxicLabels(theObject) {
return (
<h2>{theObject.label} match {theObject.results[0].match}</h2>
)
}
class App extends React.Component {
constructor(props) {
super(props)
this.state = {
objects: [],
}
}
componentDidMount() {
this.renderThePost()
}
componentDidUpdate(){
render()
}
renderThePost = async () => {
try {
let response = await toxic('you suck')
this.setState({
object: response,
})
// this.state.object.map((object)=>{
// console.log(object)
// ToxicLabels(object)
// })
}catch (err) {
console.log(err)
}
}
render() {
return (
<div>
<h2>Hello {"Hola"}</h2>
{this.state.object.map((object)=> {
console.log(object)
ToxicLabels(object)
})}
</div>
)
}
}
The Structure of data returned. This is how TensorFlow's toxicity model returns data. There is 7 objects in the array. Inside the each object there is a label and an array of results which shows the match(true or false) and the probabilities.
{
"object": [
{
"label": "identity_attack",
"results": [
"{match: false, probabilities: Float32Array(2)}"
]
},
{
"label": "insult",
"results": [
"{match: true, probabilities: Float32Array(2)}"
]
},
{
"label": "obscene",
"results": [
"{match: null, probabilities: Float32Array(2)}"
]
},
{
"label": "severe_toxicity",
"results": [
"{match: false, probabilities: Float32Array(2)}"
]
},
{
"label": "sexual_explicit",
"results": [
"{match: null, probabilities: Float32Array(2)}"
]
},
{
"label": "threat",
"results": [
"{match: false, probabilities: Float32Array(2)}"
]
},
{
"label": "toxicity",
"results": [
"{match: true, probabilities: Float32Array(2)}"
]
}
]
}
This code works form me, just exclude the toxic function and use your own.
import React from "react"
const toxic = () => {
return {
"object": [
{
"label": "identity_attack",
"results": [
"{match: false, probabilities: Float32Array(2)}"
]
},
{
"label": "insult",
"results": [
"{match: true, probabilities: Float32Array(2)}"
]
},
{
"label": "obscene",
"results": [
"{match: null, probabilities: Float32Array(2)}"
]
},
{
"label": "severe_toxicity",
"results": [
"{match: false, probabilities: Float32Array(2)}"
]
},
{
"label": "sexual_explicit",
"results": [
"{match: null, probabilities: Float32Array(2)}"
]
},
{
"label": "threat",
"results": [
"{match: false, probabilities: Float32Array(2)}"
]
},
{
"label": "toxicity",
"results": [
"{match: true, probabilities: Float32Array(2)}"
]
}
]
}
}
function ToxicLabels(theObject) {
return (
<h2>{theObject.label} match {theObject.results[0].match}</h2>
)
}
export default class App extends React.Component {
constructor() {
super()
this.state = {
render: false,
objects: [],
}
}
componentDidMount() {
this.renderThePost()
}
componentDidUpdate(){
this.render()
}
renderThePost = async () => {
try {
let response = await toxic('you suck')
console.log( response )
this.setState({
objects: response.object,
render: true
})
// this.state.object.map((object)=>{
// console.log(object)
// ToxicLabels(object)
// })
}catch (err) {
console.log(err)
}
}
render() {
if( !this.state.render ) { return <div></div> }
return (
<div>
<h2>Hello {"Hola"}</h2>
{this.state.objects.map((object) => {
return ToxicLabels(object)
})}
</div>
)
}
}
This could work out.
this.setState({
objects: response.object,
})
...
{this.state.objects.map((object)=> ToxicLabels(object))}

Filtering objects array by nested values

I'm trying to filter this objects array and keep the original one aside.
{"departments":
[
{
“name": “AAA",
“selected”: true,
"courses": [
{
"name": “course1",
“selected”: true,
“titles”:
[{
"name": “title1",
“selected”: true
},
{
"name": “title2",
“selected”: false
}]
},
{
"name": “course2",
“selected”: false,
“titles”:
[{
"name": “title1",
“selected”: false
}]
}
]
},
{
“name": “BBB",
“selected”: false,
"courses": [{...}]
{...}
]
}
I want to find all the selected departments, courses and titles. And it should be in the same format.
I tried with below code, but it change original data. I want to keep that aside too.
const depts = departments.filter((dept: any) => {
if (dept.selected) {
dept.courses = dept.courses.filter((course: any) => {
if (course.selected) {
if (course.titles) {
course.titles = course.titles.filter(({selected}: any) => selected);
}
return true;
}
return false;
});
return true;
}
return false;
});
What would be considered the best solution in this case?
Shorter alternative can be to use the JSON.parse reviver parameter :
var arr = [{ name: "AAA", selected: true, courses: [{name: "course1", selected: true, titles: [{ name: "title1", selected: true }, { name: "title1", selected: false }]}, { name: "course2", selected: false, titles: [{ name: "title1", selected: false }]}]}]
var result = JSON.parse(JSON.stringify(arr), (k, v) => v.map ? v.filter(x => x.selected) : v)
console.log( result )
your filtering logic seems to be correct. only problem is that code changes original array. in order to overcome this problem just create a deep clone of original array and run filtering logic on it
filterArray() {
const clone = JSON.parse(JSON.stringify(this.departments));
const depts = clone.filter((dept: any) => {
if (dept.selected) {
dept.courses = dept.courses.filter((course: any) => {
if (course.selected) {
if (course.titles) {
course.titles = course.titles.filter(({ selected }: any) => selected);
}
return true;
}
return false;
});
return true;
}
return false;
});
console.log(depts);
}
here is a demo https://stackblitz.com/edit/angular-xx1kp4
const filterSelected = obj => {
return {
...obj,
departments: obj.departments.map(dep => {
return {
...dep,
courses: dep.courses.map(course => {
return {
...course,
titles: course.titles.filter(title => title.selected),
};
}).filter(course => course.selected),
};
}).filter(dep => dep.selected),
};
}
const all = {
departments: [
{
name: "AAA",
selected: true,
courses: [
{
name: "course1",
selected: true,
titles: [
{
name: "title1",
selected: true
}, {
name: "title1",
selected: false
}
]
}, {
name: "course2",
selected: false,
titles: [
{
name: "title1",
selected: false
}
]
},
]
}
]
};
console.log(filterSelected(all));
I don't know if you prefer an API false. Here is my tip:
You can to use an API Json Server.
Install JSON Server
npm install -g json-server
Create a db.json file with some data
{
"posts": [
{ "id": 1, "title": "json-server", "author": "typicode" }
],
"comments": [
{ "id": 1, "body": "some comment", "postId": 1 }
],
"profile": { "name": "typicode" }
}
Start JSON Server
json-server --watch db.json
Now if you go to http://localhost:3000/posts/1, you'll get
{ "id": 1, "title": "json-server", "author": "typicode" }
you can search your array of objects using various shapes and it will come filtered. More about the API here: https://github.com/typicode/json-server
(Use a filter to do your searches on the Angular, it will bring you right what you need, use a method inside your component)

How to change the format of JSON in the front-end in React

I have JSON called by fetch request that looks like this:
[{
"type": "1",
"First": {
"Id": "123456"
}
},
{
"type": "2",
"Second": [{
"Id": "13333"
},
{
"Id": "255555"
},
{
"Id": "37777"
},
{
"Id": "48888"
}
]
}
]
What I did is that I find object with type 1 and add it to object which type 2 and splice my array to just have an object which type 2. Now it looks like this:
[{
"type": "2",
"First": {
"Id": "123456"
},
"Second": [{
"Id": "13333"
},
{
"Id": "255555"
},
{
"Id": "37777"
},
{
"Id": "48888"
}
]
}]
I have two problems. First I want to add obeject type 1 to every objects of array second, like this:
[{
"type": "2",
"Second": [{
"Id": "13333",
"First": {
"Id": "123456"
}
},
{
"Id": "255555",
"First": {
"Id": "123456"
}
},
{
"Id": "37777",
"First": {
"Id": "123456"
}
},
{
"Id": "48888",
"First": {
"Id": "123456"
}
}
]
}]
Secondly I want my JSON to just included Second array, like this:
[{
"Id": "13333",
"First": {
"Id": "123456"
}
},
{
"Id": "255555",
"First": {
"Id": "123456"
}
},
{
"Id": "37777",
"First": {
"Id": "123456"
}
},
{
"Id": "48888",
"First": {
"Id": "123456"
}
}
]
How can I solve these two issues? Here is a piece of my code:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
data: [],
library: null,
perPage: 20,
currentPage: 1,
maxPage: null,
}
}
componentDidMount() {
fetch('/json.bc', {
method: 'get',
})
.then(response => response.text())
.then(text => {
const Maindata = JSON.parse(text.replace(/\'/g, '"'))
const type1 = Maindata.find(({ type }) => type === '1');
const MergedData = Maindata.map(item => item.type === '1' ? item : { ...type1, ...item });
const MergedData2 = MergedData.splice(1)
this.setState(state => ({
...state,
data: MergedData2
}), () => {
this.reorganiseLibrary()
})
}).catch(error => console.error(error))
}
reorganiseLibrary = () => {
const { perPage, data } = this.state;
let library = data;
library = _.chunk(library, perPage);
this.setState({
library,
currentPage: 1,
maxPage: library.length === 0 ? 1 : library.length
})
}
previousPage = event => {
this.setState({
currentPage: this.state.currentPage - 1
})
}
nextPage = event => {
this.setState({
currentPage: this.state.currentPage + 1
})
}
handlePerPage = (evt) =>
this.setState({
perPage: evt.target.value
}, () => this.reorganiseLibrary());
renderLibrary = () => {
const { library, currentPage } = this.state;
if (!library || (library && library.length === 0)) {
return <div>NOResult</div>
}
return library[currentPage - 1].map((item, i) => (
<div className="Wrapper">{item.id}</div>
))
}
render() {
const { library, currentPage, perPage, maxPage } = this.state;
return (
<div>
{this.renderLibrary()}
<ul id="page-numbers">
<li>
{currentPage !== 1 && (
<button onClick={this.previousPage}></button>
)}
</li>
<li>{this.state.currentPage}</li>
<li>{this.state.maxPage}</li>
<li>
{(currentPage < maxPage) && (
<button onClick={this.nextPage}></button>
)}
</li>
</ul>
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById('Result'))
You could get the First from an item with type: 1 using find. Then filter the type: 2 items and create an new array of objects with First nested in every object in Second
const input=[{"type":"1","First":{"Id":"123456"}},{"type":"2","Second":[{"Id":"13333"},{"Id":"255555"},{"Id":"37777"},{"Id":"48888"}]}]
const { First } = input.find(a => a.type === "1") || {}
const output1 = input.filter(a => a.type === "2")
.map(a => {
const Second = a.Second.map(b => ({ ...b, First }))
return { ...a, Second }
})
console.log(JSON.stringify(output1, null, 3))
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can easily get the second output like output1[0].Second. Depends on how many items with type: 2 will be there in the array.

Jspdf-autotable Rowspan Fix

I have been generating PDF using Jspdf and Jspdf-autotable plugin. Check this code that I am using to make my customized pdf -
import React, { Component } from 'react';
import jsPDF from "jspdf";
import autotable from "jspdf-autotable";
var getColumns = function () {
return [
{ title: "Student", dataKey: "studentName" },
{ title: "Gender", dataKey: "studentGender" },
{ title: "Mother Name", dataKey: "motherName" },
{ title: "Father Name", dataKey: "fatherName" }
]
};
var getData = function () {
return rows
};
var rows;
class FeaturePage extends Component {
constructor() {
super();
this.exportpdf = this.exportpdf.bind(this);
this.state = {
sales: [
{
"studentId": "100122000116",
"name": "hasan",
"customStudentId": "1510020",
"studentName": "Tasnim Tabassum",
"studentGender": "Female",
"studentDOB": "2012-07-27",
"studentReligion": "Islam",
"motherName": "Sb",
"fatherName": "Md. Mamunar Rashid"
},
{
"studentId": "100122000116",
"name": "hasan",
"customStudentId": "1510020",
"studentName": "Star",
"studentGender": "Female",
"studentDOB": "2012-07-27",
"studentReligion": "Islam",
"motherName": "Sd",
"fatherName": "Md. Mamunar Rashid"
},
{
"studentId": "100122000116",
"name": "arif",
"customStudentId": "1510020",
"studentName": "Tasnim Tabassum",
"studentGender": "Female",
"studentDOB": "2012-07-27",
"studentReligion": "Islam",
"motherName": "safd",
"fatherName": "Md. Mamunar Rashid"
},
{
"studentId": "100122000216",
"name": "arif",
"customStudentId": "1510000",
"studentName": "Star2",
"studentGender": "Female",
"studentDOB": "2012-06-30",
"studentReligion": "Islam",
"motherName": "Mst. Fawalia Akter",
"fatherName": "Md. Azaharul Islam"
}
]
};
}
exportpdf() {
var doc = new jsPDF('p', 'pt');
doc.autoTable(getColumns(), getData(), {
theme: 'grid',
startY: 60,
drawRow: function (row, data) {
if (data.row.raw.name) {
doc.autoTableText(data.row.raw.name, data.settings.margin.left + data.table.width / 2, row.y + row.height / 2, {
halign: 'center',
valign: 'middle'
}
);
data.cursor.y += 20;
}
},
});
doc.save('Student List.pdf');
}
render() {
rows = this.state.sales;
return (
<div>
<button onClick={this.exportpdf} className="exportPDF">Export to PDF</button>
</div>
);
}
}
export default FeaturePage;
And it is generating this pdf
But I want to generate something like this one
How can I do that. I have tried out all the hooks that i Read in the documentation , but nonetheless couldn't make it work the way i want it.

Better way to achieve desired results for the following code

So the scenario is that I have this following array as my input:
[
{
"failureMessage": "failed",
"data": {
"statusCode": 201,
"body": {
"id": "14975",
"key": "KEY-4855"
}
},
"testSetName": "search"
},
{
"failureMessage": null,
"data": {
"statusCode": 201,
"body": {
"id": "14975",
"key": "KEY-4856"
}
},
"testSetName": "download"
},
{
"failureMessage": "failed 2",
"data": {
"statusCode": 201,
"body": {
"id": "14975",
"key": "KEY-4857"
}
},
"testSetName": "search"
},
{
"failureMessage": null,
"data": {
"statusCode": 201,
"body": {
"id": "14975",
"key": "KEY-4858"
}
},
"testSetName": "download"
},
{
"failureMessage": "failed",
"data": {
"statusCode": 201,
"body": {
"id": "14975",
"key": "KEY-4859"
}
},
"testSetName": "backgrounds"
},
{
"failureMessage": null,
"data": {
"statusCode": 201,
"body": {
"id": "14975",
"key": "KEY-4860"
}
},
"testSetName": "backgrounds"
}
]
the above array contains objects which hold the data regarding test cases
And I want an array like this:
[
{
"testSetName": "search",
"testCases": ["KEY-4855", "KEY-4857"]
},
{
"testSetName": "download",
"testCases": ["KEY-4856", "KEY-4858"]
},...
]
The above array contains objects and those objects hold the name of the test set(testSetName) and all the test cases which are part of this test set as testCases.
Now, my code is below:
let results = testCasesObject.reduce(function (previousArr, currElement, currIndex) {
if (previousArr.length === 0) {
let obj = {testSetName: currElement.testSetName, testCases: []};
obj.testCases.push(currElement.data.body.key);
previousArr.push(obj);
}
else {
let isAdded = false;
for (let index = 0; index < previousArr.length; index += 1) {
if (previousArr[index].testSetName === currElement.testSetName) {
previousArr[index].testCases.push(currElement.data.body.key);
isAdded = true;
}
}
if (!isAdded) {
let obj = {testSetName: currElement.testSetName, testCases: []};
obj.testCases.push(currElement.data.body.key);
previousArr.push(obj);
}
}
return previousArr;
}, []);
console.log(results);
My code generates accurate results but I want to make it more efficient and I need help on this.
You could group by testSetName and reduce the array with a lookup for an already inserted item.
var array = [{ failureMessage: "failed", data: { statusCode: 201, body: { id: "14975", key: "KEY-4855" } }, testSetName: "search" }, { failureMessage: null, data: { statusCode: 201, body: { id: "14975", key: "KEY-4856" } }, testSetName: "download" }, { failureMessage: "failed 2", data: { statusCode: 201, body: { id: "14975", key: "KEY-4857" } }, testSetName: "search" }, { failureMessage: null, data: { statusCode: 201, body: { id: "14975", key: "KEY-4858" } }, testSetName: "download" }, { failureMessage: "failed", data: { statusCode: 201, body: { id: "14975", key: "KEY-4859" } }, testSetName: "backgrounds" }, { failureMessage: null, data: { statusCode: 201, body: { id: "14975", key: "KEY-4860" } }, testSetName: "backgrounds" }],
result = array.reduce((r, { testSetName, data: { body: { key } } }) => {
var item = r.find(o => o.testSetName === testSetName);
if (item) {
item.testCases.push(key);
} else {
r.push({ testSetName, testCases: [key] });
}
return r;
}, []);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
What you want to achieve contains an algorithm often called "group by". There is a lodash implementation you can use: _.groupBy
This will give you an object indexed by "testSetName". Then you can go forward and map this to your desired structure.
const indexedByTestSetName = _.groupBy(data, item => item.testSetName)
// {search: [{testSetName: 'search', failureMessage: 'failed', data: {}}, ...]
const tests = Object.keys(indexedByTestSetName).map(key => ({
testSetName: key,
testCases: indexedByTestSetName[key]
)})
// [{testSetName: 'search', testCases: [{testSetName: 'search', failureMessage: 'failed', data: {}}, ...]}]
This is more efficient in terms of "lines of code", but how the performance compares is hard to guess. If you don't face millions of entries, I would not bother.

Categories

Resources