austrian-cpi
Version:
Query and merge Austrian CPI time series data for economic modeling
129 lines (106 loc) • 3.36 kB
JavaScript
import { austrianCPI } from "../austrianCPI.js";
import { parsePeriod, normalizeDate } from "./cpiUtils.js";
/**
* Query CPI
* @param {string} start - e.g. '2022-01' or '2022'
* @param {string} end - e.g. '2023-12' or '2023'
* @param {{ frequency?: 'monthly' | 'yearly', series?: string[] }} options
* @returns {Array<Object>}
*/
function queryCPI(start, end, options) {
options = options || {};
const frequency = options.frequency || "monthly";
const series = Array.isArray(options.series) ? options.series : [];
const startParts = start.split("-");
const endParts = end.split("-");
const startYear = parseInt(startParts[0], 10);
const startMonth = startParts.length > 1 ? parseInt(startParts[1], 10) : 1;
const endYear = parseInt(endParts[0], 10);
const endMonth = endParts.length > 1 ? parseInt(endParts[1], 10) : 12;
const min = startYear * 100 + startMonth;
const max = endYear * 100 + endMonth;
const result = [];
for (let i = 0; i < austrianCPI.length; i++) {
const row = austrianCPI[i];
const parsed = parsePeriod(row);
if (!parsed || parsed.type !== frequency) continue;
const y = parsed.year;
const m = parsed.month || 1;
const dateKey = `${y}-${m.toString().padStart(2, "0")}`;
if (dateKey < start || dateKey > end) continue;
const values = {};
for (let j = 0; j < series.length; j++) {
const key = series[j];
let val = row[key];
if (
val === "None" ||
val === undefined ||
val === null ||
typeof val === "object"
) {
values[key] = null;
} else if (typeof val === "string") {
values[key] = parseFloat(val.replace(",", "."));
} else {
values[key] = val;
}
}
result.push(Object.assign({ date: dateKey }, values));
}
return result;
}
/**
* Convert to CSV
* @param {Array<Object>} data
* @returns {string}
*/
function exportToCSV(data) {
if (!Array.isArray(data) || data.length === 0) return "";
const keys = ["date"].concat(
Object.keys(data[0]).filter((k) => k !== "date"),
);
const header = keys.join(",");
const rows = data.map((row) =>
keys
.map((k) => {
const val = row[k];
return val !== undefined && val !== null ? val : "";
})
.join(","),
);
return [header].concat(rows).join("\n");
}
/**
* Merge CPI into other dataset by date key
* @param {Array<Object>} cpiData
* @param {Array<Object>} otherData
* @returns {Array<Object>}
*/
function mergeWith(cpiData, otherData) {
const cpiMap = {};
const allKeys = new Set();
for (let i = 0; i < cpiData.length; i++) {
const row = cpiData[i];
const normDate = normalizeDate(row.date);
cpiMap[normDate] = row;
for (const key in row) {
if (key !== "date") allKeys.add(key);
}
}
return otherData.map((row) => {
const normDate = normalizeDate(row.date);
const match = cpiMap[normDate];
const merged = { ...row };
allKeys.forEach((key) => {
let val = match && key in match ? match[key] : null;
// Warn and sanitize
if (val !== null && typeof val === "object") {
console.warn("Unexpected object for key:", key, "on date:", normDate);
val = null;
}
merged[key] = val;
});
return merged;
});
}
export { queryCPI, mergeWith, exportToCSV };