Here is a version that works similarly to answers from Owen and Randy, but it separates the month-name processing into its own helper function. Written independently, the local variable names are all different, but they do the same work. It is also structured as a single function call rather than a set of steps:
const monthName = ((months) => (m) => months [m - 1])(
'01|02|03|04|05|06|07|08|09|10|11|12' .split ('|') .map (
m => new Date (`2021/${m}`).toLocaleString('default', {month: 'short'})
)
)
const transform = (xs) => {
const base = Object .fromEntries (
[...new Set(input .map (
({date}) => date .slice (3, 5)
))]
.map (month => [monthName (month), ""])
)
return Object .values (xs .reduce ((years, {date, value}) => {
const Year = date .slice (6, 10),
Month = date.slice (3, 5)
years [Year] = years [Year] || {Year, ...base}
years [Year] [monthName(Month)] = value
return years
}, {}))
}
const input = [{date: "01/01/2000", value: "1"}, {date: "01/02/2000", value: "2"}, {date: "01/01/2001", value: "2"}, {date: "01/01/2002", value: "1.5"}, {date: "01/02/2002", value: "1.6"}]
console .log (transform (input))
.as-console-wrapper {max-height: 100% !important; top: 0}
One advantage is that it doesn't call the Date constructor for every object, simply calling it once for every calendar month. And if you don't want the locale string version but a fixed set of month names the helper can be even simpler:
const monthName = ((months) => (m) => months [m - 1]) (
'Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec' .split ('|')
)
I tend to prefer functions written purely as expressions and not statement, so an alternate version might look like this:
const transform = (
xs,
base = Object .fromEntries (
[...new Set (xs .map (
({date}) => date .slice (3, 5)
))]
.map (month => [monthName (month), ""])
)
) => Object .values (
xs .reduce ((a, {date, value}, _, __,
Year = date .slice (6, 10), Month = date.slice (3, 5)
) => ({
...a,
[Year]: {Year, ...(a [Year] || base), [monthName (Month)]: value}
}), {})
)
It works the same way, and is somewhat less efficient, but I find it cleaner.