I have the following data structure from the API, it comes in as an array of Data
, each Data
element is intervaled by 1 hour.
interface Data {
time_bucket: string // ISO8601 string
aggregated_value: number // 0 and up
}
My plan is to format this for it to work with d3 to plot as a barchart. The barchart has a selector for you to group data into week
, day
, month
, and year
. I decided to create a function called groupBy
that works quite like lodash's groupBy
. It groups data to specified groups you want. This is the function
export function groupBy<T, K extends keyof any> (list: T[], criteria: (item: T) => K): Record<K, T[]> {
return list.reduce<Record<K, T[]>>((prev, curr) => {
const group = criteria(curr)
// eslint-disable-next-line
if (!prev[group]) {
prev[group] = []
}
prev[group].push(curr)
return prev
// eslint-disable-next-line
}, {} as Record<K, T[]>)
}
The problem is that the x scale of the graph is constructed in YYYY-MM-DD
format. I wanted to group the data into each day while keeping the date format to YYYY-MM-DD
. What I get right now from running the function looks like this in the snippets.
const data = [
{
time_bucket: '2021-06-01T16:00:00.000Z',
aggregated_value: 20
},
{
time_bucket: '2021-06-01T18:00:00.000Z',
aggregated_value: 20
},
{
time_bucket: '2021-06-02T16:00:00.000Z',
aggregated_value: 40
},
{
time_bucket: '2021-06-02T20:00:00.000Z',
aggregated_value: 40
},
{
time_bucket: '2021-06-03T05:00:00.000Z',
aggregated_value: 60
}
]
function groupBy(list, criteria) {
return list.reduce((prev, curr) => {
const group = criteria(curr)
if (!prev[group]) {
prev[group] = []
}
prev[group].push(curr)
return prev
}, {})
}
console.log(groupBy(data, (item) => dayjs.utc(item.time_bucket).get('date')))
<script src="https://unpkg.com/dayjs@1.8.21/dayjs.min.js"></script>
<script src="https://unpkg.com/dayjs@1.8.21/plugin/utc.js"></script>
<script>dayjs.extend(window.dayjs_plugin_utc)</script>
You can see that the output looks like this:
{
"1": [
{
"time_bucket": "2021-06-01T16:00:00.000Z",
"aggregated_value": 20
},
{
"time_bucket": "2021-06-01T18:00:00.000Z",
"aggregated_value": 20
}
],
"2": [
{
"time_bucket": "2021-06-02T16:00:00.000Z",
"aggregated_value": 40
},
{
"time_bucket": "2021-06-02T20:00:00.000Z",
"aggregated_value": 40
}
],
"3": [
{
"time_bucket": "2021-06-03T05:00:00.000Z",
"aggregated_value": 60
}
]
}
This is what I wanted
{
"2021-06-01": [
{
"time_bucket": "2021-06-01T16:00:00.000Z",
"aggregated_value": 20
},
{
"time_bucket": "2021-06-01T18:00:00.000Z",
"aggregated_value": 20
}
],
"2021-06-02": [
{
"time_bucket": "2021-06-02T16:00:00.000Z",
"aggregated_value": 40
},
{
"time_bucket": "2021-06-02T20:00:00.000Z",
"aggregated_value": 40
}
],
"2021-06-03": [
{
"time_bucket": "2021-06-03T05:00:00.000Z",
"aggregated_value": 60
}
]
}
What I wanted from the function is to be able to group the data into specified range, while still keeping the format of the date in YYYY-MM-DD
format for me to still map it to the d3
x scale that I have generated. Is there any function in dayjs
that could do this or is there any workaround. Thank you very much for the response.
After quite a while of research. I decided to round any date to the start of each time range.
I would be using it like this in the example.
const data = [
{
time_bucket: '2021-06-01T16:00:00.000Z',
aggregated_value: 20
},
{
time_bucket: '2021-06-01T18:00:00.000Z',
aggregated_value: 20
},
{
time_bucket: '2021-06-02T16:00:00.000Z',
aggregated_value: 40
},
{
time_bucket: '2021-06-02T20:00:00.000Z',
aggregated_value: 40
},
{
time_bucket: '2021-06-03T05:00:00.000Z',
aggregated_value: 60
}
]
function groupBy(list, criteria) {
return list.reduce((prev, curr) => {
const group = criteria(curr)
if (!prev[group]) {
prev[group] = []
}
prev[group].push(curr)
return prev
}, {})
}
console.log(groupBy(data, (item) => dayjs.utc(item.time_bucket).startOf('day').format('YYYY-MM-DD')))
<script src="https://unpkg.com/dayjs@1.8.21/dayjs.min.js"></script>
<script src="https://unpkg.com/dayjs@1.8.21/plugin/utc.js"></script>
<script>dayjs.extend(window.dayjs_plugin_utc)</script>
This way I can use the .format()
that got exposed from startOf()
to group the data into each time range.