HighCharts/Javascript: Resample Monthly Data Series to Quarterly/Yearly? - javascript
In a React app that uses Highcharts to draw the chart using monthly data, how can we resample the monthly data into quarterly or yearly data, then plot it using Highcharts?
If Highcharts does not provide a resampling function, the solution can use other JS libraries if needed, such as Moment.js. However, Highcharts should be not replaced with another charting library, such as Highstocks.
Thank you in advance!
Original Monthly Data:
const monthlyData = [[946702800000,261],[949381200000,251],[951886800000,282],[954565200000,289],[957153600000,259],[959832000000,259],[962424000000,256],[965102400000,264],[967780800000,289],[970372800000,291],[973054800000,254],[975646800000,275],[978325200000,250],[981003600000,254],[983422800000,227],[986101200000,262],[988689600000,248],[991368000000,237],[993960000000,257],[996638400000,250],[999316800000,239],[1001908800000,253],[1004590800000,235],[1007182800000,259],[1009861200000,259],[1012539600000,264],[1014958800000,243],[1017637200000,240],[1020225600000,214],[1022904000000,238],[1025496000000,251],[1028174400000,255],[1030852800000,259],[1033444800000,248],[1036126800000,260],[1038718800000,268],[1041397200000,246],[1044075600000,259],[1046494800000,264],[1049173200000,273]]
Minimal Reproducible Code
export function Chart({ data }) {
const options = {
title: { text: 'Time series data' },
xAxis: { type: 'datetime' },
}
options.series = [{
name: 'foo',
type: 'line',
data: [[946702800000,261],[949381200000,251],[951886800000,282],[954565200000,289],[957153600000,259],[959832000000,259],[962424000000,256],[965102400000,264],[967780800000,289],[970372800000,291],[973054800000,254],[975646800000,275],[978325200000,250],[981003600000,254],[983422800000,227],[986101200000,262],[988689600000,248],[991368000000,237],[993960000000,257],[996638400000,250],[999316800000,239],[1001908800000,253],[1004590800000,235],[1007182800000,259],[1009861200000,259],[1012539600000,264],[1014958800000,243],[1017637200000,240],[1020225600000,214],[1022904000000,238],[1025496000000,251],[1028174400000,255],[1030852800000,259],[1033444800000,248],[1036126800000,260],[1038718800000,268],[1041397200000,246],[1044075600000,259],[1046494800000,264],[1049173200000,273]],
}]
return (
<HighchartsReact
highcharts={Highcharts}
options={options}
/>
)
}
You need to calculate quarterly and yearly data manually. Below you can find a simple example how you can do that in React. I have used isSameQuarter and isSameYear methods from date-fns library.
function Chart() {
const [options, setOptions] = useState({...});
const getProcessedData = (method) => {
const processedData = [];
const monthlyData = getData();
monthlyData.forEach((el, index) => {
if (
index === 0 ||
!method(new Date(el[0]), new Date(monthlyData[index - 1][0]))
) {
processedData.push(el);
} else {
processedData[processedData.length - 1][1] += el[1];
}
});
return processedData;
};
const yearlyData = useMemo(() => getProcessedData(isSameYear), []);
const quarterlyData = useMemo(() => getProcessedData(isSameQuarter), []);
const setData = (period) => {
const processedData =
period === "month"
? getData()
: period === "year"
? yearlyData
: quarterlyData;
setOptions({
series: [{
data: processedData
}]
});
};
return (
<>
<HighchartsReact highcharts={Highcharts} options={options} />
<button onClick={() => setData("month")}>Monthly Data</button>
<button onClick={() => setData("quarter")}>Quarterly Data</button>
<button onClick={() => setData("year")}>Yearly Data</button>
</>
);
}
Live demo: https://codesandbox.io/s/highcharts-react-demo-forked-w6g6b?file=/demo.jsx
Docs:
https://date-fns.org/v2.17.0/docs/isSameQuarter
https://date-fns.org/v2.17.0/docs/isSameYear
Related
How can i see my array on my component ? / React / Javascript
I have a element like : const DropdownElements = [ { key: 1, title: "Şehir", placeholder: "Şehir Seçiniz", apiUrl: "https://api.npoint.io/995de746afde6410e3bd", type: "city", selecteditem: "", data : [], }, { key: 2, title: "İlçe", placeholder: "İlçe Seçiniz", apiUrl: "https://api.npoint.io/fc801dbd3fc23c2c1679", // its my apis. They hold datas from json type: "district", selecteditem: "", data : [], }, ] I fetching that url in App in useEffect. const App = () => { useEffect(() => { DropdownElements.map((x) => { fetch(x.apiUrl) .then((z) => z.json()) .then((vb) => { x.data=vb // If i write x.data.push(vb) i can see it on my component but its not giving pure array. console.log(x.data) // I can see my datas perfectly. I trying fill my data. }); }); }, []); And i setting it like that : <Space> {DropdownElements.map((x) => { return ( <PickerCompanent showSearch selecteditem={idhold} key={x.key} placeholder={x.placeholder} type={x.type} datasource={x.data} // I gave my datasource x.data that i filled . onFocus={onFocus} onChange={z=>onChange(z)} onFocus={onFocus} onSearch={onSearch} ></PickerCompanent> ); })} </Space> But in my component when i try write like console.log(props) my datasource is empty array. How can i see my datas on my component ? I need set my array to a state in my component.
It seems like you aren't using any kind of state in your code. const App = () => { const [myData, setMyData] = useState(); useEffect(() => { DropdownElements.map((x) => { fetch(x.apiUrl) .then((z) => z.json()) .then((vb) => { x.data=vb // If i write x.data.push(vb) i can see it on my component but its not giving pure array. console.log(x.data) // I can see my datas perfectly. I trying fill my data. // in here you'll want to be adding your data to some state // e.g. setMyData(x.data); }); }); }, []); Then within your component, use that state: datasource={myData}
Your object is updating but not view. To achieve this you need have a component state, to which we can update and trigger return again to update view. const App = () => { const [myData, setMyData] = useState(DropdownElements); useEffect(() => { myData.map((x, i) => { fetch(x.apiUrl) .then((z) => z.json()) .then((result) => { myData[i].data = result; setMyData(myData); }); }); }, []); return ( <Space> {myData.map((x) => { return ( <PickerCompanent showSearch selecteditem={idhold} key={x.key} placeholder={x.placeholder} type={x.type} datasource={x.data} // I gave my datasource x.data that i filled . onFocus={onFocus} onChange={z=>onChange(z)} onFocus={onFocus} onSearch={onSearch} ></PickerCompanent> ); })} </Space> );
Having problems with Chart.js and Canvas
I am currently using Graph.js to render graphs it is working on the initial render, but until I press setTimeformats buttons in order to show another graph on the same canvas, it is giving me Error: Canvas is already in use. Chart with ID '0' must be destroyed before the canvas can be reused. Am I using it properly? How Should I destroy the chart in order to use other graphs on the same canvas? Help would be very appreciated. import React, { useRef, useEffect, useState } from "react"; import { historyOptions } from "../chartConfig/chartConfig"; import Chart from 'chart.js/auto'; interface Props{ data:any } const ChartData:React.FC<Props> = ({ data}) => { const chartRef = useRef<HTMLCanvasElement | null>(null); const { day, week, year, detail } = data; const [timeFormat, setTimeFormat] = useState("24h"); const determineTimeFormat = () => { switch (timeFormat) { case "24h": return day; case "7d": return week; case "1y": return year; default: return day; } }; useEffect(() => { if (chartRef && chartRef.current && detail) { const chartInstance = new Chart(chartRef.current, { type: "line", data: { datasets: [ { label: `${detail.name} price`, data: determineTimeFormat(), backgroundColor: "rgba(174, 305, 194, 0.5)", borderColor: "rgba(174, 305, 194, 0.4", pointRadius: 0, }, ], }, options: { ...historyOptions, }, }); if (typeof chartInstance !== "undefined") chartInstance.destroy(); } }); const renderPrice = () => { if (detail) { return ( <> <p className="my-0">${detail.current_price.toFixed(2)}</p> <p className={ detail.price_change_24h < 0 ? "text-danger my-0" : "text-success my-0" } > {detail.price_change_percentage_24h.toFixed(2)}% </p> </> ); } }; return ( <div className="bg-white border mt-2 rounded p-3"> <div>{renderPrice()}</div> <div> <canvas ref={chartRef} id="myChart" width={250} height={250}></canvas> </div> <div className="chart-button mt-1"> <button onClick={() => setTimeFormat("24h")} className="btn btn-outline-secondary btn-sm" > 24h </button> <button onClick={() => setTimeFormat("7d")} className="btn btn-outline-secondary btn-sm mx-1" > 7d </button> <button onClick={() => setTimeFormat("1y")} className="btn btn-outline-secondary btn-sm" > 1y </button> </div> </div> ); }; export default ChartData;
One way you might solve this problem is by using a new state variable and useEffect to quickly remove and re-create the canvas element each time the timeFormat changes. Some key points here: As #CallumMorrisson mentioned, in order to understand this approach, it is extremely important to read and understand this section of the React docs about skipping the useEffect hook in its entirety. Using the day, name, week, year attributes directly in useEffect instead of the entire data variable makes sure that the chart instance is only re-created when necessary, not on every render. Same goes for the function determineTimeFormat, those types of functions should be defined outside the component's scope if possible. const determineTimeFormat = ( timeFormat: string, day: any, week: any, year: any ) => { switch (timeFormat) { case "24h": return day; case "7d": return week; case "1y": return year; default: return day; } }; interface Props { data: any } const ChartData: React.FC<Props> = ({ data }) => { const chartCanvasRef = useRef<HTMLCanvasElement | null>(null); const { day, week, year, detail } = data; const { name } = detail; const [timeFormat, setTimeFormat] = useState("24h"); const [isRebuildingCanvas, setIsRebuildingCanvas] = useState(false); // remove the canvas whenever timeFormat changes useEffect(() => { setIsRebuildingCanvas(true); }, [timeFormat]); // timeFormat must be present in deps array for this to work /* if isRebuildingCanvas was true for the latest render, it means the canvas element was just removed from the dom. set it back to false to immediately re-create a new canvas */ useEffect(() => { if (isRebuildingCanvas) { setIsRebuildingCanvas(false); } }, [isRebuildingCanvas]); useEffect(() => { const chartCanvas = chartCanvasRef.current if (isRebuildingCanvas || !chartCanvas) { return; } const chartInstance = new Chart(chartRef.current, { type: "line", data: { datasets: [ { label: `${name} price`, data: determineTimeFormat(timeFormat, day, week, year), backgroundColor: "rgba(174, 305, 194, 0.5)", borderColor: "rgba(174, 305, 194, 0.4", pointRadius: 0, }, ], }, options: { ...historyOptions, }, }); return () => { chartInstance.destroy(); } }, [day, isRebuildingCanvas, name, timeFormat, week, year]); return ( <> {isRebuildingCanvas ? undefined : ( <canvas ref={chartCanvasRef} id='myChart' width={250} height={250} /> )} <button onClick={() => setTimeFormat("24h")}>24h</button> <button onClick={() => setTimeFormat("7d")}>7d</button> <button onClick={() => setTimeFormat("1y")}>1y</button> </> ); }; export default ChartData;
React How can i make new array when i choose some item from picker?
I thinking for few days but cant realize how can i make it. I have 4 json data and 4 picker. Its for city,district,village,neirborhood. In first i must choose city then in second picker it must show district about that i choose city. When i choose district from picker third one must show villages about that district. And neirborhood is same too. In that json datas they have some connection. Like city json have ' id-name' district have 'id-cityid-name' village have 'id-districtid-name' neirborhood have 'id-villageid-name' Like that. But i cant figure out how can i make it. Its my codes I really stuck with that hardly i need some help please. Thank you! My codes : Elements : const DropdownElements = [ { key: 1, title: "Şehir", placeholder: "Şehir Seçiniz", apiUrl: "https://api.npoint.io/995de746afde6410e3bd", type: "city", selecteditem: "", data : [], }, { key: 2, title: "İlçe", placeholder: "İlçe Seçiniz", apiUrl: "https://api.npoint.io/fc801dbd3fc23c2c1679", type: "district", selecteditem: "", data : [], }, { key: 3, title: "Köy", placeholder: "Köy Seçiniz", apiUrl: "https://api.npoint.io/72cf025083b70615b8bb", type: "village", selecteditem: "", data : [], }, { key: 4, title: 'Mahalle', placeholder:'Mahalle Seçiniz', apiUrl: 'https://api.npoint.io/0c04c63923c8ca4e117b', type: 'neighborhood', selecteditem: "", data : [], }, ]; Component : const PickerCompanent = (props) => { const [xdata, setData] = useState([]); const [newData, setNewData] = useState([]); let x; let y = []; // data.filter((a) => a.il_id == "3"); useEffect(() => { props.datasource.then(setData); switch (props.type) { case "city": x = props.selecteditem; setNewData(xdata); break; case "district": y = xdata.filter((element) => { if (props.selecteditem === element.id) { return element; } }); break; case "village": console.log("village"); break; default: console.log("def"); break; } }, [props.datasource]); return ( <Select showSearch style={{ width: 200, marginLeft: 15 }} placeholder={props.placeholder} optionFilterProp="children" onChange={(x) => props.onChange(x)} onFocus={props.onFocus()} datasource={xdata} onSearch={props.onSearch()} filterOption={(input, option) => option.children.toLowerCase().indexOf(input.toLowerCase()) >= 0 } > {xdata && xdata.map((x) => { return ( <Select.Option key={x.id} value={x.id}> {x.name}{" "} </Select.Option> ); })} </Select> ); }; My App : const App = () => { const [dataap, setDataAp] = useState([]); const [idhold, setIDHold] = useState(); const filldata = (value) => {}; function onChange(value) { setIDHold(value); console.log(value); } const getData = (value, type) => { return fetch(value) .then((x) => x.json()) .then((y) => { return y; }); }; function onFocus() {} function onSearch(val) {} return ( <Space> {DropdownElements.map((x) => { return ( <PickerCompanent showSearch selecteditem={idhold} key={x.key} placeholder={x.placeholder} type={x.type} datasource={getData(x.apiUrl)} onFocus={onFocus} onChange={(z) => onChange(z)} onFocus={onFocus} onSearch={onSearch} ></PickerCompanent> ); })} </Space> ); }; If you need i can give my teamviewer or skype too. I really need that help thanks for replies! Sandbox : codesandbox.io/s/runtime-monad-vxit
https://codesandbox.io/s/mystifying-moore-7w105?file=/src/App.js Select CityTwo to see the dropdown update. You need a switch. Updating arrays inside state is tricky. You can't populate or push anything in an array that's in state. Update your array outside state, THEN update state.
Ant Design Table component not displaying state-based dataSource change
I am using Ant Design for my React project and I'm having trouble with the Table component. I have a list of tasks to which I add a new task based on a Form content - currently just by adding to an array of objects (taskListMock in the code snippets), the app is not linked to any backend. The form works fine, however, the Table does not refresh, even though the dataSource prop of the Table gets its content directly from the state and the state updates correctly - confirmed by logging and devtools. Curiously, the table refreshes with the new task when I initiate the implemented sorting, so my suspicion is that the Table somehow does not refresh its content from the state change, only on onChange hooks or something, but I'm feeling in a bit of a dead-end - any help would be greatly appreciated since I'm planning to use similar functionality in other Tables. The structure is pretty simple, I have a TasksIndex.js with the Table as an individual component in TaskListTable.js TaskListTable.js: const TaskListTable = (props) => { const { t } = useTranslation(); const [tableContent, setTableContent] = useState(props.tasks); return ( <React.Fragment> <Table pagination={false} dataSource={tableContent} columns={[ { title: t("tasks.name"), key: "name", render: (text) => { return <p>{text.slug}</p>; }, }, { title: t("tasks.dateDue"), dataIndex: "dateDue", key: "dateDue", sorter: (a, b) => new Date(a.dateDue).getTime() - new Date(b.dateDue).getTime(), render: (dateDue) => { let dateFormatted = moment(dateDue); return <>{dateFormatted.format("LL")}</>; }, defaultSortOrder: "ascend", }, { title: t("tasks.priority"), key: "priority", dataIndex: "priority", render: (priority) => ( <React.Fragment> {priority === "low" ? ( <Tag color="geekblue">{t("tasks.lowPriority")}</Tag> ) : ( "" )} {priority === "normal" ? ( <Tag color="green">{t("tasks.normalPriority")}</Tag> ) : ( "" )} {priority === "high" ? ( <Tag color="volcano">{t("tasks.highPriority")}</Tag> ) : ( "" )} </React.Fragment> ), sorter: (a, b) => { const priorityOrder = ["low", "normal", "high"]; return ( priorityOrder.indexOf(a.priority) - priorityOrder.indexOf(b.priority) ); }, }, { title: t("tasks.options"), key: "options", render: (item) => { return ( <Checkbox value={item.id}>{t("tasks.setCompleted")}</Checkbox> ); }, }, ]} ></Table> </React.Fragment> ); }; export default TaskListTable; TaskIndex.js: const TasksIndex = () => { const [isModalOpen, setModalOpen] = useState(false); const [taskList, updateTaskList] = useState(taskListMock); const [form] = Form.useForm(); const addTask = useCallback( (values) => { const newTaskList = taskList; newTaskList.push({ id: taskList[taskList.length - 1] + 1, slug: values.name, description: values.description, dateDue: values.dateDue.format("YYYY-MM-DD"), priority: values.priority, checked: false, }); form.resetFields(); updateTaskList(newTaskList); closeModal(); }, [taskList, form] ); const openModal = () => { setModalOpen(true); }; const closeModal = () => { setModalOpen(false); }; const { t } = useTranslation(); return ( <React.Fragment> <Title>{t("tasks.tasksOverviewHeader")}</Title> <Row gutter={[16, 24]}> <Col className="gutter-row" span={24}> <TaskListTable tasks={taskList}></TaskListTable> </Col> </Row> ... ...
I finally fixed it - it seems that creating a new array and pushing the new task to it was not considered a state change (or perhaps a Table change trigger), unlike using the spread operator. The working code looks like this: const addTask = (values) => { const newTask = { id: taskList[taskList.length - 1] + 1, slug: values.name, description: values.description, dateDue: values.dateDue.format("YYYY-MM-DD"), priority: values.priority, checked: false, }; updateTaskList([...taskList, newTask]); closeModal(); form.resetFields(); };
Data visualization in ReactJs with ChartJs
I am new in reactjs. Currently I'm developing an app which shows json COVID-19 api data into visualization using chartjs. I tried this from yesterday but I can't show the visual data. Here is my code App Component import React, { useState, useEffect } from "react"; import axios from "axios"; import Chart from "./Chart"; const App = () => { const [state, setState] = useState({}); const [loading, setLoading] = useState(true); const [chart, setChart] = useState({}); useEffect(() => { getData("italy"); setChart({ labels: ["Cases", "Deaths", "Recovered"], datasets: [ { label: "cases", data: [state.cases] }, { label: "deaths", data: [state.deaths] }, { label: "recovered", data: [state.recovered] } ] }); }, []); const getData = async country => { try { const res = await axios.get( `https://corona.lmao.ninja/v2/historical/${country}` ); setLoading(false); setState(res.data.timeline); } catch (error) { console.log(error.response); } }; return ( <div> {!loading ? console.log( "cases", state.cases, "deaths", state.deaths, "recovered", state.recovered ) : null} {!loading ? <Chart chart={chart} /> : "loading failed"} </div> ); }; export default App; And Here is Chart Component import React from "react"; import { Line } from "react-chartjs-2"; const Chart = ({chart}) => { return ( <div> <Line data={chart} height={300} width={200} options={{ maintainAspectRatio: false, title: { display: true, text: "Covid-19", fontSize: 25 }, legend: { display: true, position: "top" } }} /> </div> ); }; export default Chart; If I open browser and dev tools it look likes this I want to visualize the data like this Here is codeSandBox.io
Looks like data property within dataset takes only array of numbers. I have simplifies your code using class based component. It will help you get started. https://codesandbox.io/s/react-chartjs-2-example-mzh9o setChartData = () => { let { data } = this.state; let chartData = { labels: ["Cases", "Deaths", "Recovered"], datasets: [ { label: "cases", data: Object.values(data.cases) }, { label: "deaths", data: Object.values(data.deaths) }, { label: "recovered", data: Object.values(data.recovered) } ] }; this.setState({ chart: chartData }); };