I want to custom highcharts, but before that I am stuck how to combine or manage multiple arrays with the same date and name value from JSON
// sample data JSON
const dataJson = [
{
"day": 1,
"month": 10,
"name": "John",
"value": 1000.0
},
{
"day": 1,
"month": 10,
"name": "Joe",
"value": 2000.0
},
{
"day": 2,
"month": 10,
"name": "John",
"value": 2000
},
{
"day": 2,
"month": 10,
"name": "Joe",
"value": 500
},
{
"day": 2,
"month": 10,
"name": "Joe",
"value": 500
},
{
"day": 2,
"month": 10,
"name": "Jane",
"value": 500
},
{
"day": 3,
"month": 10,
"name": "John",
"value": 1500
},
{
"day": 3,
"month": 10,
"name": "John",
"value": 2000
},
{
"day": 4,
"month": 10,
"name": "Jane",
"value": 1500
},
{
"day": 5,
"month": 10,
"name": "Janet",
"value": 1000
}
]
I want implement the above JSON into this highchart function:
Like merge day and month then show it in xAxis categories be day/month.
ex: day 1 and month 10 => 1/10.
Merge value same name in same day. Sample JSON day 2 Joe has 500 and 500. So in highchart series value for Joe is 1000 for day 2
Highcharts.chart('container', {
chart: {
type: 'column'
},
title: {
text: 'Total fruit consumption, grouped by gender'
},
xAxis: {
categories: ['1/10', '2/10', '3/10', '4/10', '5/10']
},
yAxis: {
allowDecimals: false,
min: 0,
title: {
text: 'Number of fruits'
}
},
tooltip: {
formatter: function () {
return '<b>' + this.x + '</b><br/>' +
this.series.name + ': ' + this.y + '<br/>' +
'Total: ' + this.point.stackTotal;
}
},
plotOptions: {
column: {
stacking: 'normal'
}
},
series: [{
name: 'John',
data: [1000, 2000, 3500, 0, 0],
}, {
name: 'Joe',
data: [2000, 1000, 0, 0, 0],
}, {
name: 'Jane',
data: [0, 500, 0, 1500, 0],
}, {
name: 'Janet',
data: [0, 0, 0, 0, 1000],
}]
});
Here is link to my fiddle
Thanks
Ok, this way is maybe not the most performant to accomplish this task, but it gets the job done:
const dataJson = [{
day: 1,
month: 10,
name: "John",
value: 1000.0,
},
{
day: 1,
month: 10,
name: "Joe",
value: 2000.0,
},
{
day: 2,
month: 10,
name: "John",
value: 2000,
},
{
day: 2,
month: 10,
name: "Joe",
value: 500,
},
{
day: 2,
month: 10,
name: "Joe",
value: 500,
},
{
day: 2,
month: 10,
name: "Jane",
value: 500,
},
{
day: 3,
month: 10,
name: "John",
value: 1500,
},
{
day: 3,
month: 10,
name: "John",
value: 2000,
},
{
day: 4,
month: 10,
name: "Jane",
value: 1500,
},
{
day: 5,
month: 10,
name: "Janet",
value: 1000,
},
];
const res = dataJson.reduce((acc, cV) => {
if (acc[cV.name]) {
acc[cV.name] = {
day: {
...acc[cV.name].day,
[cV.day]: acc[cV.name].day[cV.day] ?
acc[cV.name].day[cV.day] + cV.value :
cV.value,
},
};
} else {
acc[cV.name] = {
day: {
[cV.day]: cV.value
}
};
}
return acc;
}, {});
let result = [];
Object.keys(res).forEach(name => {
const values = {
name
};
const data = [];
for (let i = 1; i <= 5; i++) {
if (res[name].day[i]) {
data.push(res[name].day[i]);
} else {
data.push(0);
}
}
values.data = data;
result.push(values);
});
console.log(result);
In the res variable, I change the array of json data into an object with the name and day as keys. If the day exists, I add the new amount to the current one, otherwise I create the key.
After that, I loop through the object and check whether values exist in a given day. If yes, then I push it to the data, if no, I push 0 to the data. My solution is for 5 days, but you can easily adjust it for any given amount of days.
Related
I have a state with an array of objects. Objects represent workers. Each worker has a name property and a nested object (projects2021). Inside the nested object there's an array of objects (projectsList):
[{
name: 'John',
projects2021: {
hours: 15,
projectsAll: 10,
projectsNames: ['x', 'y', 'z'],
projectsList: [
{
month: 'january',
projectName: 'germany',
status: 'vodja',
hours: 50,
},
{
month: 'february',
projectName: 'germany',
status: 'vodja',
hours: 50,
},
],
},
},
{
name: 'David',
projects2021: {
hours: 15,
projectsAll: 10,
projectsNames: ['x', 'y', 'z'],
projectsList: [
{
month: 'january',
projectName: 'germany',
status: 'vodja',
hours: 50,
},
{
month: 'february',
projectName: 'germany',
status: 'vodja',
hours: 50,
},
],
},
}
]
const [workers, setWorkers] = useState(users);
I would like to map over the state and render the workers in a way where only the projects with a given month would be shown. For example, if I clicked "january", I would like react to display all the workers (their names), but only the projects with a "month" property of "january". In layman's terms: filtering the table by a given month. This is what I've done so far:
const filterByMonth = (month) => {
let mainArray = [];
workers.map((worker) => {
const result = worker.projects2021.projectsList.filter(
(data) => data.month === month
);
mainArray.push(result)
});
setWorkers(mainArray);
};
With my approach I mutate the state directly (which is not ok) and thus loose certain parts of an object. I want to retain my object and only change the state of a nested array of objects (projectsList).
I was thinking of a way where I would spread the object first and then concat/push the nested array inside of an object.
I do apologize if my question isn't structured the way it should be, but this is my first time posting and I am a self-taught fella :).
Thank you
You can use spread to achieve it. Here's a sample:
const filterByMonth = (month) => {
const result = workers.map((worker) => {
return {...worker , projects2021: worker.projects2021.projectsList.filter(data => data.month === month)}
});
setWorkers(result);
};
One way of accomplishing this is to have workers as constant and filtered workers as state.
const workers= [{
name: 'John',
projects2021: {
hours: 15,
projectsAll: 10,
projectsNames: ['x', 'y', 'z'],
projectsList: [
{
month: 'january',
projectName: 'germany',
status: 'vodja',
hours: 50,
},
],
},
{...}]
const [filteredWorkers, setFilteredWorkers]=useState(workers)
Then apply filtering to the state
const filterByMonth = (month) => {
let newWorkers = workers.filter((worker)=> worker.projects2021.projectsList.month === month);
setFilteredWorkers(newWorkers);
You actually can use this code:
const workers = [{
name: 'John',
projects2021: {
hours: 15,
projectsAll: 10,
projectsNames: ['x', 'y', 'z'],
projectsList: [
{
month: 'january',
projectName: 'germany',
status: 'vodja',
hours: 50,
},
{
month: 'february',
projectName: 'germany',
status: 'vodja',
hours: 50,
},
],
},
},
{
name: 'David',
projects2021: {
hours: 15,
projectsAll: 10,
projectsNames: ['x', 'y', 'z'],
projectsList: [
{
month: 'january',
projectName: 'germany',
status: 'vodja',
hours: 50
},
{
month: 'february',
projectName: 'germany',
status: 'vodja',
hours: 50
},
],
},
}];
const updatedWorkers = workers.map(worker => {
const updatedProjectsList = worker.projects2021.projectsList.filter(item =>
item.month === 'january'
);
return ({...worker, projects2021:{...worker.projects2021, projectsList: updatedProjectsList}});
});
console.log(updatedWorkers)
I have an first array like this
[
["Maths", "Chemistry", "Physics"],
["CS", "EC"],
["High", "Medium", "Low", "Average", "Excellent"]
]
And I have an another array of object in the below format
[
[{
id: 1,
name: "Maths",
is_active: 1
},
{
id: 2,
name: "Chemistry",
is_active: 1
},
{
id: 3,
name: "Physics",
is_active: 1
},
{
id: 4,
name: "Social Science",
is_active: 1
}
],
[{
id: 10,
name: "CS",
is_active: 1
},
{
id: 11,
name: "EC",
is_active: 1
},
{
id: 12,
name: "PHY",
is_active: 1
},
],
[{
id: 101,
name: "High",
is_active: 1
},
{
id: 102,
name: "Low",
is_active: 1
},
{
id: 103,
name: "Medium",
is_active: 1
},
{
id: 104,
name: "Excellent",
is_active: 1
},
{
id: 105,
name: "Average",
is_active: 1
},
{
id: 106,
name: "Below Average",
is_active: 1
},
]
]
I need to replace the first array values with id by matching the names present in first array with name present in the nested array of objects in second array.
My Final Output need to be in this format
[
[1,2,3],
[10,11],
[101,103,102,105,104]
]
Can Anyone help me how to do this in TypeScript.
I can suggest using map() and find() this :
simpleData is your table containing only the names
fullData is your table containing your objects
let fullData = [
[{
id: 1,
name: "Maths",
is_active: 1
},
{
id: 2,
name: "Chemistry",
is_active: 1
},
{
id: 3,
name: "Physics",
is_active: 1
},
{
id: 4,
name: "Social Science",
is_active: 1
}
],
[{
id: 10,
name: "CS",
is_active: 1
},
{
id: 11,
name: "EC",
is_active: 1
},
{
id: 12,
name: "PHY",
is_active: 1
},
],
[{
id: 101,
name: "High",
is_active: 1
},
{
id: 102,
name: "Low",
is_active: 1
},
{
id: 103,
name: "Medium",
is_active: 1
},
{
id: 104,
name: "Excellent",
is_active: 1
},
{
id: 105,
name: "Average",
is_active: 1
},
{
id: 106,
name: "Below Average",
is_active: 1
},
]
]
let simpleData = [
["Maths", "Chemistry", "Physics"],
["CS", "EC"],
["High", "Medium", "Low", "Average", "Excellent"]
]
let newIdTable = [];
for ( let i = 0; i < fullData.length; i++ ) {
let table = simpleData[i].map( ( name ) => {
return fullData[i].find( item => item.name === name ).id
} );
newIdTable.push( table );
}
console.log(newIdTable)
Using find(), if the corresponding object doesn't exist it will return undefined. I didn't test the case here, because I supposed that your object already exist in the fullData table. So you have to modify the code with a condition to handle that case if you need :)
I have an array that I want to group according to type then count the number of instances for every unique status.
Here is my array:
const array = [
{ type: 'A', number: 2, status:'Approved', year: '2020' },
{ type: 'A', number: 3, status:'Approved', year: '2020' },
{ type: 'A', number: 3, status:'Disapproved', year: '2020' },
{ type: 'A', number: 6, status:'Disapproved', year: '2020' },
{ type: 'A', number: 5, status:'Processing', year: '2020' },
{ type: 'B', number: 8, status:'Processing', year: '2020' },
{ type: 'B', number: 2, status:'Approved', year: '2020' },
{ type: 'B', number: 2, status:'Disapproved', year: '2020' },
]
Here is my desired array:
const desiredArray = [
{ type: 'A', Approved:5, Disapproved: 9, Processing: 13, year: '2020' },
{ type: 'B', Approved:2, Disapproved: 2, year: '2020' },
]
With help from other online friends, this worked perfectly:
const reducedObject = array.reduce((rv,x) =>{
if(!rv[x.type]) {
rv[x.type] = {
type: x.type,
Approved: 0,
Disapproved: 0,
Processing: 0,
year: x.year
}
}
rv[x.type][x.status]++;
return rv;
}, {});
const desiredArray = Object.values(reducedObject);
Another answer:
var array = [
{ Id: "001", qty: 1 },
{ Id: "002", qty: 2 },
{ Id: "001", qty: 2 },
{ Id: "003", qty: 4 }
];
var result = [];
array.reduce(function(res, value) {
if (!res[value.Id]) {
res[value.Id] = { Id: value.Id, qty: 0 };
result.push(res[value.Id])
}
res[value.Id].qty += value.qty;
return res;
}, {});
console.log(result)
I have created a areaspline chart using highcharts library and making it animated with play/pause button transition between weeks of data
Ref. https://www.highcharts.com/blog/tutorials/176-charts-in-motion/
jsfiddle Ref. https://jsfiddle.net/larsac07/wkev75nL/?utm_source=website&utm_medium=embed&utm_campaign=wkev75nL
in the above example, we are animating the chart week wise
dataSet used :
dataSequence = [
{
name: 'Week 1',
data: [1, 2, 2, 1, 1, 2, 2]
}, {
name: 'Week 2',
data: [6, 12, 2, 3, 3, 2, 2]
}, {
name: 'Week 3',
data: [4, 5, 6, 5, 5, 4, 9]
}, {
name: 'Week 4',
data: [5, 5, 6, 6, 5, 6, 6]
}, {
name: 'Week 5',
data: [6, 7, 7, 6, 6, 6, 7]
}, {
name: 'Week 6',
data: [8, 9, 9, 8, 8, 8, 9]
}, {
name: 'Week 7',
data: [9, 10, 4, 10, 9, 9, 9]
}, {
name: 'Week 8',
data: [1, 10, 10, 10, 10, 11, 11]
}, {
name: 'Week 9',
data: [11, 11, 12, 12, 12, 11, 11]
}
]
I don't want change chart
on play button click I want animate the data points 11 data point for week1 to same data point but different value on y axis for week2
xAxis = ["week1", "Week2", ..... ],
yAxis = [[1,2,3,4,5,6,7,8,9,10,11], [3,5,7,8,2,1,5,7,6,1,10], ....]
on the play button it would transit between week1 then will go to week 2 and so till last week number available.
Trying to have something like this Ref. https://aatishb.com/covidtrends/
this chart is plotted using this dataset for series
Highcharts.chart("container", {
chart: {
type: "areaspline"
},
tooltip: {
shared: true,
valueSuffix: " units"
},
xAxis: {
categories: [
"Week 1",
"Week 2",
"Week 3",
"Week 4",
"Week 5",
"Week 6",
"Week 7"
]
},
yAxis: {
title: {
text: "Index"
}
},
legend: {
layout: "horizontal",
align: "right",
verticalAlign: "top",
x: 50,
y: 50,
floating: true,
borderWidth: 1,
backgroundColor:
(Highcharts.theme && Highcharts.theme.legendBackgroundColor) ||
"#FFFFFF"
},
plotOptions: {
areaspline: {
fillOpacity: 0.5
}
},
credits: {
enabled: false
},
series: [
{
name: "By week",
data: dataSequence[value].data.slice()
},
{
type: "spline",
name: "Topic 1",
data: [3, 2, 1, 3, 4, 7, 8]
},
{
type: "spline",
name: "Topic 2",
data: [1, 5, 1, 3, 4, 7, 8]
},
{
type: "spline",
name: "Topic 3",
data: [3, 7, 1, 3, 4, 7, 8]
},
{
type: "spline",
name: "Topic 4",
data: [5, 1, 1, 3, 4, 7, 8]
},
{
type: "spline",
name: "Topic 5",
data: [7, 3, 1, 3, 4, 7, 8]
},
{
type: "spline",
name: "Topic 6",
data: [9, 2, 1, 3, 4, 7, 8]
},
{
type: "spline",
name: "Topic 7",
data: [11, 8, 1, 3, 4, 7, 8]
},
{
type: "spline",
name: "Topic 8",
data: [13, 11, 1, 3, 4, 7, 8]
},
{
type: "spline",
name: "Topic 9",
data: [15, 7, 1, 3, 4, 7, 8]
},
{
type: "spline",
name: "Topic 10",
data: [7, 5, 1, 3, 4, 7, 8]
}
],
title: {
text: ""
},
subtitle: {
text: "Efficiency Index of Topics"
}
});
this my update function in react
update(increment) {
var input = $("#play-range")[0];
var output = $("#play-output")[0];
if (increment) {
input.value = parseInt(input.value) + increment;
}
output.innerHTML = this.state.dataSequence[input.value].name;
this.setState({
value: input.value
});
if (input.value >= input.max) {
// Auto-pause
this.pause();
this.setState(
{
value: 0
},
() => {
output.innerHTML = this.state.dataSequence[0].name;
}
);
}
}
the whole chart is plotted at once, I need something that it should transit
first, it plots all data points for week1 then week 2 after that week 3 when I click on the play button
You need to start with an empty data and use addPoint method in the update function:
function update(increment) {
var input = $('#play-range')[0],
output = $('#play-output')[0],
increment;
chart.series[0].addPoint(dataSequence[input.value].data[actualPointIndex]);
actualPointIndex += increment;
if (actualPointIndex === 6) {
actualPointIndex = 0;
input.value = parseInt(input.value) + increment;
}
output.innerHTML = dataSequence[input.value].name; // Output value
if (input.value >= input.max) { // Auto-pause
pause($('#play-pause-button')[0]);
}
}
Live demo: https://jsfiddle.net/BlackLabel/stpxyfca/
API Reference: https://api.highcharts.com/class-reference/Highcharts.Series#addPoint
I'm creating a spfx web part using Reactjs. I have a function getting an array of items from a SharePoint list that includes a number column of "Hours". I need to get a total for all the hours that have been returned but can't figure out how to calculate that.
I feel like I'm missing something simple but I've run through all kinds of loops and for some reason I can't get it to work. I've verified that I am getting data from the Hours column.
I'll also state the obligatory "I'm new to spfx and react". :) TIA for any help!
private readItem(): void {
this.props.spHttpClient.get(`${this.props.siteUrl}/_api/web/lists/getbytitle('Time Off')/items?$select=Title,Id,Hours`,
SPHttpClient.configurations.v1,
{
headers: {
'Accept': 'application/json;odata=nometadata',
'odata-version': ''
}
}).then((response: SPHttpClientResponse): Promise<ITimeOffItem[]> => {
return response.json();
})
.then((item: ITimeOffItem[]): void => {
console.log(item); //the data is here including Hours
this.setState({
items: item,
hoursTotal: //????How do I get the sum of "Hours" and assign it to a number in state
});
});
}
Create a function to loop through the items and add the hours
function countHours(items) {
if (!items) {
return 0;
}
let total = 0;
for (let i = 0; i < items.length; i++) {
total += items[i].Hours;
}
return total;
}
const item = [
{ Id: 25, Title: "Comp Time", Hours: 6, ID: 25 },
{ Id: 26, Title: "Comp Time", Hours: 5, ID: 26 },
{ Id: 27, Title: "Work from Home", Hours: 3, ID: 27 },
{ Id: 28, Title: "Comp Time", Hours: 7, ID: 28 },
{ Id: 29, Title: "Work from Home", Hours: 8, ID: 29 },
{ Id: 30, Title: "Holiday", Hours: 8, ID: 30 },
{ Id: 31, Title: "Work from Home", Hours: 32, ID: 31 }
];
console.log(countHours(item));
Use it like
this.setState({
items: item,
hoursTotal: countHours(item)
});
you can also use reduce
const item = [
{ Id: 25, Title: "Comp Time", Hours: 6, ID: 25 },
{ Id: 26, Title: "Comp Time", Hours: 5, ID: 26 },
{ Id: 27, Title: "Work from Home", Hours: 3, ID: 27 },
{ Id: 28, Title: "Comp Time", Hours: 7, ID: 28 },
{ Id: 29, Title: "Work from Home", Hours: 8, ID: 29 },
{ Id: 30, Title: "Holiday", Hours: 8, ID: 30 },
{ Id: 31, Title: "Work from Home", Hours: 32, ID: 31 }
];
const sum = item.reduce(function(a, b) { return a + b.Hours; }, 0);
console.log(sum)
It's hard to answer this without knowing what the data structure looks like, but if you are trying to sum an array of numbers you could use reduce.
const hours = [7, 5, 3, 1, 7]
const totalHours = hours.reduce((accumulator, hour) => accumulator + hour)