次図の様に何かしらの範囲から何かしらの範囲を見て、範囲同士に重複している部分があるか、あるならばどのような範囲かを識別するための考え方とコードを紹介します。これは長さを扱うもの、特にスケジュールの様な時刻と時間を同時に扱う時によく出番があります。
意味合い的な整理は図でほとんど完結しています。上記4パターンを網羅する条件式を記述すれば、重複範囲の有無とどの様に重複しているかが識別できます。例えば、PHP のコードに起こすと次の様になります。コードの構成は単純な比較、条件式、関数のみなので他言語にも容易に流用できます。
/** * A が B の左端に被っているならば true */ function isCoveredOnLeftSide($aStart, $aEnd, $bStart, $bEnd): bool { return $aStart <= $bStart && $bStart <= $aEnd && $aEnd <= $bEnd; } /** * A が B の右端に被っているならば true */ function isCoveredOnRightSide($aStart, $aEnd, $bStart, $bEnd): bool { return $bStart <= $aStart && $aStart <= $bEnd && $bEnd <= $aEnd; } /** * A が B に包括されているならば true */ function isIncluded($aStart, $aEnd, $bStart, $bEnd): bool { return $bStart <= $aStart && $aEnd <= $bEnd; } /** * A が B を包括しているならば true */ function isInclude($aStart, $aEnd, $bStart, $bEnd): bool { return $aStart <= $bStart && $bEnd <= $aEnd; }
また、単に重複範囲を持つか持たないかだけを識別したいのであれば次の様に書けます。
// 計算コストを気にしないで良いのであれば function hasOverlap($aStart, $aEnd, $bStart, $bEnd): bool { return isCoveredOnLeftSide($aStart, $aEnd, $bStart, $bEnd) || isCoveredOnRightSide($aStart, $aEnd, $bStart, $bEnd) || isIncluded($aStart, $aEnd, $bStart, $bEnd) || isInclude($aStart, $aEnd, $bStart, $bEnd);; } // 処理短縮パターン function hasOverlap($aStart, $aEnd, $bStart, $bEnd): bool { if($aStart <= $bStart) { if($bEnd <= $aEnd) { return true;// A が B を包括 } if($bStart <= $aEnd) { return true;// A が B の左端に被っている } } else { if($aEnd <= $bEnd) { return true;// B が A を包括 } if($aStart <= $bEnd) { return true;// A が B の右端に被っている } } return false; }
追記: SQL 上でやるなら BETWEEN 演算子を使って
"A の左端が B の左端から右端の間にある" OR "A の右端が B の左端から右端の間にある" OR ("A の左端が B の左端より左にある" AND "A の右端が B の右端より右にある")
とするとよさそう