JavaScriptで年を基準に、月、四半期、半期、年単位の期間データを動的に生成する関数を実装します。この関数は、指定された開始年から現在の年までの期間データを生成し、表示形式をカスタマイズできます。
function TimePeriodGenerator() {
let today = new Date();
let currentYear = today.getFullYear();
let currentMonth = today.getMonth() + 1;
this.data = [];
this.periodType = "";
this.startYear = "";
this.currentMonth = currentMonth;
this.currentYear = currentYear;
/**
* 関数を初期化し、指定されたパラメータに基づいて期間データを生成します
* @param {number} year - 4桁の開始年
* @param {number} type - 期間タイプ(1:月度, 2:四半期, 3:半期, 4:年度)
* @param {boolean} includeCurrent - 現在の期間を含めるかどうか
* @param {boolean} isAscending - 昇順ソートかどうか
* @param {number} sortRule - ソートルール(1:月単位, 4:年単位)
* @returns {Array} 生成された期間データ
*/
this.initialize = function(year, type, includeCurrent = false, isAscending = false, sortRule = 4) {
this.periodType = type;
this.startYear = year;
this.includeCurrent = includeCurrent;
this.data = [];
this.setStartYear();
this.setPeriodType();
let copiedData = JSON.parse(JSON.stringify(this.data));
let sortKey = sortRule === 1 ? 'value' : 'year';
this.data = copiedData.sort(this.sortByDate(sortKey, isAscending));
return this.data;
};
/**
* 期間タイプを設定し、対応するデータ生成メソッドを呼び出します
*/
this.setPeriodType = function() {
let type = this.periodType || 4;
switch(type) {
case 1:
this.periodLabel = '月';
this.generateMonthlyData();
break;
case 2:
this.periodLabel = '四半期';
this.generateQuarterlyData();
break;
case 3:
this.periodLabel = '半期';
this.generateSemiannualData();
break;
case 4:
this.periodLabel = '年度';
this.generateYearlyData();
break;
}
};
/**
* 開始年を設定します。指定がない場合は現在の年を使用します
*/
this.setStartYear = function() {
let year = this.startYear;
if (year && Object.prototype.toString.call(year) !== "[object Number]") {
year = Number(year);
} else if (!year) {
year = currentYear;
}
this.startYear = year;
};
/**
* 配列内の重複オブジェクトを削除します
* @param {Array} array - 処理対象の配列
* @param {string} key - 重複判定のためのキー
* @returns {Array} 重複削除後の配列
*/
this.removeDuplicates = function(array, key) {
const uniqueMap = {};
return array.filter(item => {
const keyValue = item[key];
if (!uniqueMap[keyValue]) {
uniqueMap[keyValue] = true;
return true;
}
return false;
});
};
/**
* 配列を指定されたキーでソートします
* @param {Array} dataArray - ソート対象の配列
* @param {string} key - ソートキー
* @returns {Array} ソート後の配列
*/
this.sortArray = function(dataArray, key) {
return dataArray.sort((a, b) => {
if (a[key].toString() !== b[key].toString()) {
return a[key].toString().localeCompare(b[key].toString());
}
return 0;
});
};
/**
* 日付データをソートします
* @param {string} property - ソート対象のプロパティ
* @param {boolean} ascending - 昇順かどうか
* @returns {Function} ソート関数
*/
this.sortByDate = function(property, ascending) {
return function(a, b) {
const value1 = a[property];
const value2 = b[property];
if (ascending) {
return Date.parse(value1) - Date.parse(value2);
} else {
return Date.parse(value2) - Date.parse(value1);
}
};
};
/**
* 年度データを生成します
*/
this.generateYearlyData = function() {
if (this.startYear === currentYear) {
this.data.push({
year: this.startYear,
value: 1,
type: this.periodType,
label: this.startYear + '年'
});
} else if (this.startYear < currentYear) {
for (let i = this.startYear; i <= currentYear; i++) {
this.data.push({
year: i,
value: 1,
type: this.periodType,
label: i + '年'
});
}
if (!this.includeCurrent && this.periodType === 4) {
this.data = this.data.filter(item => item.year !== currentYear);
}
}
};
/**
* 半期データを生成します
*/
this.generateSemiannualData = function() {
this.generateYearlyData();
let copiedData = JSON.parse(JSON.stringify(this.data));
copiedData = copiedData.concat(this.data);
let sortedData = this.sortArray(copiedData);
let deleteIndex = null;
let periodValue = 1;
let periodLabel = "上半期";
for (let i = 0; i < sortedData.length; i++) {
if (currentMonth > 6 && i % 2 === 0) {
periodValue = 1;
} else if (currentMonth > 6 && currentMonth < 12 && i % 2 !== 0) {
periodValue = 2;
}
periodLabel = periodValue === 1 ? '上半期' : '下半期';
sortedData[i].value = periodValue;
sortedData[i].label = sortedData[i].year + '年' + periodLabel;
if (sortedData[i].year === currentYear && sortedData[i].value === 2) {
deleteIndex = i;
}
}
if (!this.includeCurrent) {
sortedData.splice(deleteIndex, 1);
}
this.data = sortedData;
};
/**
* 四半期データを生成します
*/
this.generateQuarterlyData = function() {
this.generateMonthlyData();
let processedData = this.data.map(item => {
let quarter = Math.ceil(item.value / 3);
item.value = quarter;
item.label = item.year + '年' + quarter + '四半期';
return item;
});
this.data = this.removeDuplicates(processedData, 'label');
};
/**
* 月度データを生成します
*/
this.generateMonthlyData = function() {
this.generateYearlyData();
let monthlyData = [];
let copiedData = JSON.parse(JSON.stringify(this.data));
let maxMonth = currentMonth;
for (let i = 0; i < copiedData.length; i++) {
if (copiedData[i].year < currentYear) {
maxMonth = 12;
} else if (copiedData[i].year === currentYear) {
maxMonth = currentMonth;
}
for (let m = 1; m <= maxMonth; m++) {
monthlyData.push({
year: copiedData[i].year,
value: m,
type: this.periodType,
label: copiedData[i].year + '年' + m + '月'
});
}
}
if (!this.includeCurrent) {
monthlyData = monthlyData.filter(item =>
!(item.year === currentYear && item.value === currentMonth)
);
}
this.data = monthlyData;
};
}
関数の使い方
この関数を使用する前に、まずTimePeriodGeneratorをインスタンス化する必要があります。
// 関数をインスタンス化
const timeGenerator = new TimePeriodGenerator();
// データを生成(現在の月を含めない場合)
const monthlyData = timeGenerator.initialize(2023, 1, false);
// データを生成(現在の月を含める場合)
const monthlyDataWithCurrent = timeGenerator.initialize(2023, 1, true);
console.log("生成されたデータ:", timeGenerator.data);
console.log("月度データ:", monthlyData);
console.log("現在の月を含むデータ:", monthlyDataWithCurrent);
パラメータの説明
year: 4桁の開始年を文字列または数値で指定type: 期間タイプ(1:月度, 2:四半期, 3:半期, 4:年度)includeCurrent: 現在の期間を含めるかどうか(true:含める, false:含めない)isAscending: ソート順(true:昇順, false:降順)sortRule: ソートルール(1:月単位, 4:年単位)
データ生成メソッドの詳細
- generateYearlyData: 年度データを生成します。半期、月度データの生成にも呼び出されます。
- generateSemiannualData: 半期データを生成します。年度データを基にして上半期と下半期を生成します。
- generateQuarterlyData: 四半期データを生成します。月度データを基にして四半期を計算します。
- generateMonthlyData: 月度データを生成します。年度データを基にして各月のデータを生成します。
この関数は、ビジネスアプリケーションでの期間選択、レポート生成、データ分析など、様々なシナリオで活用できます。