views:

33

answers:

2

I have an array with a series of event IDs organized like this: $event['year']['month']['day'] = $event_id (full structure below).

Basically, I want to output the next 5 events (or so) from a given date (such as today).

I've gone through the PHP manual, but haven't found a suitable solution. I suppose I could just iterate through each step, but there could be hundreds of events. If I knew the offset, I could use array_slice, but I'm not sure how to get the offset without looping through the entire array. If I could set the pointer, then I would just iterate through. But I gather there isn't a way to set a pointer in a PHP array.

A specific MySQL query isn't very feasible either since the data isn't well organized (this is using meta keys in a Wordpress database). I'd probably have to use a number of JOINs, so I think the performance hit would be bad.

Given the current year, month, and day (e.g., $event[$year][$month][$day], I want to just show the next 5 events.

The structure looks like this:

Array
(
    [2010] => Array
        (
            [1] => Array
                (
                    [1] => Array
                        (
                            [594] => "Event"
                        )
                )
            [2] => Array
                (
                    [1] => Array
                        (
                            [592] => "Event",
                            [524] => "Event"
                        )

                    [2] => Array
                        (
                            [580] => "Event"
                        )
    [2011] => Array
        (
            [1] => Array
                (
                    [1] => Array
                        (
                            [587] => "Event"
                        )
                )
        )
)

Thoughts? Sorry if this description is a bit complicated. Thanks!

Edit: Typos

A: 

If the array is ordered, you could use binary search to find the date of today or the closest thing before today (if there are no entries for today).

Then, you would have to iterate through the array as if it were flat and output the next three entries.

This is better than iterating through the whole array since it doesn't take linear time.

Artefacto
Artefacto, thanks for the help! I had to read the Wikipedia article to know what you were talking about. Useful to know.In this case, the function will always know today's date (or whatever the given start date is). The problem is iterating through the multidimensional array for the 5 events after the known date. I'm trying to avoid a performance intensive iterative loop through the array (such as the one in Charles' suggestion).
dkrape
@dkrape Ah I now see the problem. You can't really navigate through the array because you don't know the name of the next entry. There's really no way to do this without traversing the whole array, because PHP unfortunately does not expose a way to set the internal pointer to a specific element so that you could then use `next` and `prev`.
Artefacto
Ah, good to know. I was playing around with next() and prev(), but I kept having to go back to a higher level of the array (e.g., starting at day, then back to month, then back to year). In the end, I was simply looping through the complete in a more convoluted fashion.
dkrape
A: 

This won't be fast because it iterates through the entire array. In fact, this is probably a bad way to do it. Unfortunately, this might be the most straightforward way. The actual date of each event isn't stored directly, but we can derive it.

$earliest_date = strtotime('next Monday'); // or whatever.
$new_events = array();
foreach($array as $year => $a) {
    foreach($array[$year] as $month => $b {
        foreach($array[$year][$month] as $day => $events) {
            $date = strtotime("$year-$month-$day");
            if($date >= $earliest_date)
                $new_events[] = array( 'date' => $date, 'events' => $events );
            if(count($new_events) >= 5)
                break 3; // Breaks out of all three loops.
        }
    }
}

Of note, the new array picks the five events on or after the date, but because there can be multiple events per day (per your sample data), it's possible that only some of the events may be in the array.

Charles
Charles, thanks for the response! Yes, this is what I was thinking to do. Unfortunately, I worry that a method like this would impact performance of the site (this will display on the home page). Perhaps the solution is to just do this and cache the result.
dkrape
Can you tell us more about what generates the array in that format? Ideally, your best solution would limit the date range that goes into the array to begin with.
Charles
Sure, the code is here: http://pastebin.com/YhpXWaAKI thought about simply adding an if/then to check if the date given is before the event date and filtering that out. However, this still requires looping through all the events in the database (worse, I think, since this has lots of database queries).This site doesn't get huge amounts of traffic (it is an intranet site), so perhaps the performance hit of the nested foreach loops in your example code won't be that bad?
dkrape
I just checked the performance of this and I'm getting between 0.01 and 0.005 seconds, which seems decent to me. I imagine with a good number more events it will slow down, hopefully not too much. In that case I'll revisit this (famous last words no doubt). Here is the final code I used: http://pastebin.com/gD38t0X9 There is a parens missing in the above example and I adjusted the contents of the $new_events array. Thanks again Charles!
dkrape
@dkrape, the first chunk source code you posted at pastebin is utterly hilarious. `$event_section` on line eight is exactly what you want. If you are able to, you can easily clone the function and slice in date filtering at that level. It's still not as great as it could be (running it at the database level), but it would be an improvement. Still, at five thousandths of a second, it's not a disaster...
Charles
I always try to bring some levity to people's lives! Unfortunately, it is often with the quality of my code.Anyhow, you think the first code would be better? I figured since it has several database queries it would be slower than just the foreach iterations. In the first code I pasted, there are four database queries per event. In the code here there are none (since the $events array will be cached).
dkrape
Charles
I'm actually using Wordpress' custom post type with meta fields for the date. That is the reason for the "get_post_meta()" functions. Each post does include dates for when the post was last added and edited, but I don't want to depend on my web editors having to change the posted on date to the actual event date.
dkrape