I'm not very clear about what you're trying to accomplish. Anyway, in the general case, you can do something like this.
The idea is to run the whole array of items, and if the "next" item is contiguous to a candidate interval you already have, you extend the interval. Else, the candidate interval becomes a standalone interval, and the item that just failed the check gives birth to a new candidate interval.
You need two functions: one that, given two items, returns whether it's true or false that they are contiguous; the other, given two items, returns an "interval" with those two items as extremes.
An empty $items
will return an empty interval.
function build_intervals($items, $is_contiguous, $make_interval) {
$intervals = array();
$end = false;
foreach ($items as $item) {
if (false === $end) {
$begin = $item;
$end = $item;
continue;
}
if ($is_contiguous($end, $item)) {
$end = $item;
continue;
}
$intervals[] = $make_interval($begin, $end);
$begin = $item;
$end = $item;
}
if (false !== $end) {
$intervals[] = $make_interval($begin, $end);
}
return $intervals;
}
For numbers, you can use
$interv = build_intervals(
array( 1, 2, 3, 5, 6, 9, 10, 11, 13, 17, 18 ),
function($a, $b) { return ($b - $a) <= 1; },
function($a, $b) { return "{$a}..{$b}"; }
);
print_r($interv);
returns
Array
(
[0] => 1..3
[1] => 5..6
[2] => 9..11
[3] => 13..13
[4] => 17..18
)
With dates, you can keep them as DateTime
and DateTimeIntervals
. If you use timestamps, then you must supply a contiguousness criterion that's valid for timestamps. This can be awkward if you have two timestamps that are just before and after midnight the following day. To be sure, you should take times at always around midday (i.e., given two dates, get the timestamps of those dates at midday. If they're less than 36 hours apart, they're two adjacent days.
function($a, $b) {
$a_ts = strtotime("{$a} 12:00:00");
$b_ts = strtotime("{$b} 12:00:00");
return ($b - $a) <= (36 * 60 * 60);
},