views:

42

answers:

3

Hi everyone.

Working with an array of dates (opening times for a business). I want to condense them to their briefest possible form.

So far, I started out with this structure

Array
(
    [Mon] => 12noon-2:45pm, 5:30pm-10:30pm
    [Tue] => 12noon-2:45pm, 5:30pm-10:30pm
    [Wed] => 12noon-2:45pm, 5:30pm-10:30pm
    [Thu] => 12noon-2:45pm, 5:30pm-10:30pm
    [Fri] => 12noon-2:45pm, 5:30pm-10:30pm
    [Sat] => 12noon-11pm
    [Sun] => 12noon-9:30pm
)

What I want to achieve is this:

Array
(
    [Mon-Fri] => 12noon-2:45pm, 5:30pm-10:30pm
    [Sat] => 12noon-11pm
    [Sun] => 12noon-9:30pm
)

I've tried writing a recursive function and have managed to output this so far:

Array
(
    [Mon-Fri] => 12noon-2:45pm, 5:30pm-10:30pm
    [Tue-Fri] => 12noon-2:45pm, 5:30pm-10:30pm
    [Wed-Fri] => 12noon-2:45pm, 5:30pm-10:30pm
    [Thu-Fri] => 12noon-2:45pm, 5:30pm-10:30pm
    [Sat] => 12noon-11pm
    [Sun] => 12noon-9:30pm
)

Can anybody see a simple way of comparing the values and combining the keys where they're similar? My recursive function is basically two nested foreach() loops - not very elegant.

Thanks, Matt

EDIT: Here's my code so far, which produces the 3rd array above (from the first one as input):

$last_time = array('t' => '', 'd' => ''); // blank array for looping
$i = 0;

foreach($final_times as $day=>$time) {

    if($last_time['t'] != $time ) { // it's a new time

        if($i != 0) { $print_times[] = $day . ' ' . $time; } 
        // only print if it's not the first, otherwise we get two mondays

    } else { // this day has the same time as last time

        $end_day = $day;

        foreach($final_times as $day2=>$time2) {

            if($time == $time2) {
                $end_day = $day2;
            }

        }

        $print_times[] = $last_time['d'] . '-' . $end_day . ' ' . $time;

    }

$last_time = array('t' => $time, 'd' => $day);
$i++;

}
+1  A: 

I don't think there is a particularly elegant solution to this. After much experimenting with the built in array_* functions trying to find a nice simple solution, I gave up and came up with this:

$lastStart = $last = $lastDay = null;
$new = array();

foreach ($arr as $day => $times) {
 if ($times != $last) {
  if ($last != null) {
   $key = $lastStart == $lastDay ? $lastDay : $lastStart . '-' . $lastDay;
   $new[$key] = $last;
  }
  $lastStart = $day;
  $last = $times;
 }
 $lastDay = $day;
}

$key = $lastStart == $lastDay ? $lastDay : $lastStart . '-' . $lastDay;
$new[$key] = $last;

It only uses one foreach loop as opposed to your two, as it keeps a bunch of state. It'll only merge adjacent days together (i.e., you won't get something like Mon-Tue,Thu-Fri if Wednesday is changed, you'll get two separate entries).

Chris Smith
You're a hero, thank you! I'll have a skim through this and see if I can find a way to modify it to cater for those edge cases like you mention. Thank you for helping :)
Matt Andrews
+1  A: 

I'd approach it by modelling it as a relational database:

day      start        end
1        12:00        14:45
1        17:30        22:30
...

Then its fairly easy to reduce - there are specific time intervals:

SELECT DISTINCT start, end FROM timetable;

And these will occur on specific days:

SELECT start, end, GROUP_CONCAT(day) ORDER BY day SEPERATOR ',' FROM timetable GROUP BY start,end

(this uses the MySQL-only 'group_concat' function - but the method is the same where this is not available) would give:

12:00    14:45  1,2,3,4,5
17:30    22:30  1,2,3,4,5
12:00    23:00  6
12:00    21:30  7

Then it's fairly simple to work out consecutive date ranges from the list of days.

C.

symcbean
Thanks - these dates are already coming from a relational database, and I had to work with them to turn them into the form I posted above (basically tweak the dates and group together multiple times for the same day). I might have a look at doing more on the SQL side but for now I think Chris Smith's solution above is the way to go.
Matt Andrews
+1  A: 
Chris Smith
Well, now I'm glad I didn't get around to attempting to do this myself - I'd never have the skill to figure out all of those anonymous functions and array_* calls. Thank you!One issue: the code gives me this error:`Parse error: syntax error, unexpected T_FUNCTION, expecting ')'` on the line starting: function ($mydays) use ($days, $dayIndices) {Any ideas?
Matt Andrews
@Matt Anonymous functions like the ones splattered throughout that code were introduced in PHP 5.3.0. I guess you're using an earlier version. If you really want to do it this way, you'll need to pull the anonymous functions out into proper functions (and use globals instead of closures). I think it'd be easier to modify the other solution though ;)
Chris Smith