はじめに
実務で病院やクリニック、サロンなど、定休日を指定して、それ以外の営業日を整形して表示したいという場面がありました。
たとえば、
- 定休日が日曜だけなら「月曜 ~ 土曜」
- 定休日が水曜だけなら「日曜 ~ 火曜、木曜 ~ 土曜」
- 定休日が不定休なら「定休日なし(不定休)」
といった表示が求められるケースです。
そこで今回は、定休日の配列から営業日を "◯曜 ~ ◯曜、◯曜"
のように整形して返す TypeScript 関数を紹介します。
🎯 ゴール
以下のような enum
をベースに:
enum ClinicClosedDays {
SUNDAY = "日曜日",
MONDAY = "月曜日",
TUESDAY = "火曜日",
WEDNESDAY = "水曜日",
THURSDAY = "木曜日",
FRIDAY = "金曜日",
SATURDAY = "土曜日",
IRREGULAR = "不定休"
}
このような関数を実装します:
```ts
getOpenDayRanges([ClinicClosedDays.SUNDAY, ClinicClosedDays.WEDNESDAY, ClinicClosedDays.SATURDAY]);
// → "月曜 ~ 火曜、木曜 ~ 金曜"
🔧 実装コード
function getOpenDayRanges(closedDays: ClinicClosedDays[]): string {
const allDays = [
ClinicClosedDays.SUNDAY,
ClinicClosedDays.MONDAY,
ClinicClosedDays.TUESDAY,
ClinicClosedDays.WEDNESDAY,
ClinicClosedDays.THURSDAY,
ClinicClosedDays.FRIDAY,
ClinicClosedDays.SATURDAY,
];
if (closedDays.includes(ClinicClosedDays.IRREGULAR)) {
return "定休日なし(不定休)";
}
const openDays = allDays.filter(day => !closedDays.includes(day));
if (openDays.length === 0) {
return "全日休診";
}
const dayIndexMap = new Map<ClinicClosedDays, number>([
[ClinicClosedDays.SUNDAY, 0],
[ClinicClosedDays.MONDAY, 1],
[ClinicClosedDays.TUESDAY, 2],
[ClinicClosedDays.WEDNESDAY, 3],
[ClinicClosedDays.THURSDAY, 4],
[ClinicClosedDays.FRIDAY, 5],
[ClinicClosedDays.SATURDAY, 6],
]);
const sortedOpenDays = openDays.sort((a, b) => (dayIndexMap.get(a) ?? 0) - (dayIndexMap.get(b) ?? 0));
const ranges: string[] = [];
let rangeStart = sortedOpenDays[0];
let prevIndex = dayIndexMap.get(rangeStart)!;
for (let i = 1; i <= sortedOpenDays.length; i++) {
const currentDay = sortedOpenDays[i];
const currentIndex = dayIndexMap.get(currentDay);
if (currentIndex !== prevIndex + 1) {
const rangeEnd = sortedOpenDays[i - 1];
ranges.push(formatRange(rangeStart, rangeEnd));
rangeStart = currentDay;
}
prevIndex = currentIndex ?? -999;
}
return ranges.join("、");
function formatRange(start: ClinicClosedDays | undefined, end: ClinicClosedDays | undefined): string {
const formatDay = (day: ClinicClosedDays | undefined) =>
day ? day.replace("曜日", "曜") : "";
return start === end ? formatDay(start) : `${formatDay(start)} ~ ${formatDay(end)}`;
}
}
処理の流れイメージ
たとえば以下のような入力:
const closedDays = [SUNDAY, WEDNESDAY, SATURDAY];
開いている曜日は:
[MONDAY, TUESDAY, THURSDAY, FRIDAY]
この場合、範囲分けは次のように行われます:
解説付きのコードとテストケース
// 定休日を表す enum(日本語表記)
enum ClinicClosedDays {
SUNDAY = "日曜日",
MONDAY = "月曜日",
TUESDAY = "火曜日",
WEDNESDAY = "水曜日",
THURSDAY = "木曜日",
FRIDAY = "金曜日",
SATURDAY = "土曜日",
IRREGULAR = "不定休"
}
/**
* 定休日の配列から営業日を整形し、"月曜 ~ 火曜、木曜 ~ 金曜" のように返す関数
*
* @param closedDays - 定休日の配列
* @returns 営業日を範囲形式で整形した文字列
*/
function getOpenDayRanges(closedDays: ClinicClosedDays[]): string {
// 全曜日を定義(日曜始まり)
const allDays = [
ClinicClosedDays.SUNDAY,
ClinicClosedDays.MONDAY,
ClinicClosedDays.TUESDAY,
ClinicClosedDays.WEDNESDAY,
ClinicClosedDays.THURSDAY,
ClinicClosedDays.FRIDAY,
ClinicClosedDays.SATURDAY,
];
// 不定休が含まれる場合は特別なメッセージを返す
if (closedDays.includes(ClinicClosedDays.IRREGULAR)) {
return "定休日なし(不定休)";
}
// 定休日を除いた曜日が営業日となる
const openDays = allDays.filter(day => !closedDays.includes(day));
// 営業日が0日の場合(毎日休診)
if (openDays.length === 0) {
return "全日休診";
}
// 各曜日に対応するインデックス(日曜: 0 ~ 土曜: 6)
const dayIndexMap = new Map<ClinicClosedDays, number>([
[ClinicClosedDays.SUNDAY, 0],
[ClinicClosedDays.MONDAY, 1],
[ClinicClosedDays.TUESDAY, 2],
[ClinicClosedDays.WEDNESDAY, 3],
[ClinicClosedDays.THURSDAY, 4],
[ClinicClosedDays.FRIDAY, 5],
[ClinicClosedDays.SATURDAY, 6],
]);
// 営業日をインデックス順に並べ替え
const sortedOpenDays = openDays.sort(
(a, b) => (dayIndexMap.get(a) ?? 0) - (dayIndexMap.get(b) ?? 0)
);
// 営業日の連続範囲を格納する配列
const ranges: string[] = [];
// 最初の範囲の開始点
let rangeStart = sortedOpenDays[0];
// 前の曜日のインデックスを保持
let prevIndex = dayIndexMap.get(rangeStart)!;
// 営業日をループして範囲に分割
for (let i = 1; i <= sortedOpenDays.length; i++) {
const currentDay = sortedOpenDays[i];
const currentIndex = dayIndexMap.get(currentDay);
// 前の曜日と連続していない場合 → 範囲の区切り
if (currentIndex !== prevIndex + 1) {
const rangeEnd = sortedOpenDays[i - 1];
ranges.push(formatRange(rangeStart, rangeEnd));
// 次の範囲のスタートを更新
rangeStart = currentDay;
}
// インデックスを更新(undefined の場合はダミー)
prevIndex = currentIndex ?? -999;
}
// 最終的に「月曜 ~ 水曜、金曜」形式で返す
return ranges.join("、");
// 単体または範囲を "月曜" または "月曜 ~ 水曜" 形式に整形
function formatRange(start: ClinicClosedDays | undefined, end: ClinicClosedDays | undefined): string {
const formatDay = (day: ClinicClosedDays | undefined) =>
day ? day.replace("曜日", "曜") : "";
return start === end ? formatDay(start) : `${formatDay(start)} ~ ${formatDay(end)}`;
}
}
// テスト用ラッパー関数:定休日を渡して結果を表示
function test(closed: ClinicClosedDays[], label: string) {
const result = getOpenDayRanges(closed);
const formattedClosed = closed.map(d => d.replace("曜日", "曜")).join("・");
console.log(`🧪 ${label}`);
console.log(`定休日: ${formattedClosed || "なし"}\n→ 営業日: ${result}\n`);
}
// 豊富なケースを用意
test([], "全て営業(定休日なし)");
test([ClinicClosedDays.SUNDAY], "日曜休み");
test([ClinicClosedDays.WEDNESDAY], "水曜休み");
test([ClinicClosedDays.SUNDAY, ClinicClosedDays.SATURDAY], "土日休み");
test([ClinicClosedDays.MONDAY, ClinicClosedDays.TUESDAY, ClinicClosedDays.THURSDAY, ClinicClosedDays.FRIDAY], "月・火・木・金休み");
test([ClinicClosedDays.SUNDAY, ClinicClosedDays.WEDNESDAY, ClinicClosedDays.SATURDAY], "日・水・土休み");
test([
ClinicClosedDays.SUNDAY,
ClinicClosedDays.MONDAY,
ClinicClosedDays.TUESDAY,
ClinicClosedDays.WEDNESDAY,
ClinicClosedDays.THURSDAY,
ClinicClosedDays.FRIDAY,
ClinicClosedDays.SATURDAY
], "全休診");
test([ClinicClosedDays.IRREGULAR], "不定休");
test([ClinicClosedDays.TUESDAY, ClinicClosedDays.THURSDAY], "火・木休み");
test([ClinicClosedDays.SUNDAY, ClinicClosedDays.MONDAY], "日・月休み");
test([ClinicClosedDays.FRIDAY], "金曜休み");
🧪 全て営業(定休日なし)
定休日: なし
→ 営業日: 日曜 ~ 土曜
🧪 日曜休み
定休日: 日曜
→ 営業日: 月曜 ~ 土曜
🧪 水曜休み
定休日: 水曜
→ 営業日: 日曜 ~ 火曜、木曜 ~ 土曜
🧪 土日休み
定休日: 日曜・土曜
→ 営業日: 月曜 ~ 金曜
🧪 月・火・木・金休み
定休日: 月曜・火曜・木曜・金曜
→ 営業日: 日曜、水曜、土曜
🧪 日・水・土休み
定休日: 日曜・水曜・土曜
→ 営業日: 月曜 ~ 火曜、木曜 ~ 金曜
🧪 全休診
定休日: 日曜・月曜・火曜・水曜・木曜・金曜・土曜
→ 営業日: 全日休診
🧪 不定休
定休日: 不定休
→ 営業日: 定休日なし(不定休)
🧪 火・木休み
定休日: 火曜・木曜
→ 営業日: 日曜 ~ 月曜、水曜、金曜 ~ 土曜
🧪 日・月休み
定休日: 日曜・月曜
→ 営業日: 火曜 ~ 土曜
🧪 金曜休み
定休日: 金曜
→ 営業日: 日曜 ~ 木曜、土曜
処理一部を解説(日本語解説)
- 営業日を「連続する範囲」に分割
例:
- 月・火・水・金・土 → 月 ~ 水、金 ~ 土
- 日・水・金 → 日曜、水曜、金曜
処理手順:
- 最初の営業日を rangeStart とする
- ループで次の営業日と前の営業日を比較
3.「連続してない」と判断されたら、そこまでの範囲を切り出して ranges に追加
4.次の営業日を新しい rangeStart にする
5.最後の要素まで確認したら join("、") して返す
終わりに
株式会社シンシアでは、実務未経験のエンジニアの方や学生エンジニアインターンを採用し一緒に働いています。
※ シンシアにおける働き方の様子はこちら
弊社には年間100人程度の実務未経験の方に応募いただき、技術面接を実施しております。
この記事が少しでも学びになったという方は、ぜひ wantedly のストーリーもご覧いただけるととても嬉しいです!