duandushang5148
duandushang5148
2014-06-25 23:28

使用两个值检查整个数组的子数组?

已采纳

I have a mixed array made up of events. Example:

 Array
(
    [0] => Array
        (
            [start] => 20:00
            [end] => 21:00
            [title] => test event with a date
            [date] => 22 Jun 2014
        )

    [1] => Array
        (
            [start] => 20:00
            [end] => 22:00
            [title] => test event without a date
            [day] => Sunday
        )

)

Most events are daily (e.g. Sunday). However, some are occasional and have a full date.

If an event with a full date falls on the same day and time as a daily event (as in the example above), I would like the daily event not to show up when I step through the array in a foreach.

Is there a way to check if both the date and time exist in an item somewhere else in the array before outputting?

This is what I have so far:

// loop through each day of week
    foreach ($thisweek as $day => $v) {
        echo '<h3>' . $day . ' ' . $v . '</h3>';
        echo '<ul>';
        // loop through a bunch of hours
        foreach ($eventhours as $eventhour) {
            // loop through events
            foreach ($mergedevents as $event) {

                // if event date is on this day / hour
                if ($event['date'] == $v && $event['start'] == $eventhour) {
                        echo '<li><strong>' . $eventhour . ' - ' . $event['title'] . '</strong></li>';
                } 

                // if daily event has this day / hour
                if ($event['day'] == $day && $event['start'] == $eventhour) {
                        echo '<li>' . $eventhour . ' - ' . $event['title'] . '</li>';
                };

            }
        }
        echo '</ul>';
    }

This works fine, but both occasional and daily events show up regardless. I guess I need to wrap the if($event['day'] == $day) part in another if that looks through the whole array. Is that possible some how?

  • 点赞
  • 写回答
  • 关注问题
  • 收藏
  • 复制链接分享
  • 邀请回答

2条回答

  • dongqichang7988 dongqichang7988 7年前

    Tested code on PHP 5.3.18. later version where everything is functions and includes a sort and reduction to a simple output array

    Download the source code for the updated version with the sort function.

    demonstration of the code shown below at viper-7

    Required: Given two events for the same day, one specific to the date and one the repeats on the day, then the event with specific date must be used.

    Check the time of the events for any day so that only non-overlapping events are accepted and preference is given to 'run-once' events.

    Assumptions: the input is for one week. The order of the events in the input is not important.

    The output array will be dependent on the order of the input. i.e. it is not certain to be in day order.

    The time of the events must be taken into account when adding events.

    The algorithm: There are lots of comments in the code explaining the actual checks performed. It is not as simple as i thought initially.

    Run Once Event: This must not overlap with any other event for the day and is added over any similar 'repeat' event for the day.

    repeated events: only add to output if output day / time is empty. These will get overwritten by 'specific date' events for the same day / time.

    Code:

    /*
     * Hold a list of events by day.
     * Each 'day' will be a list of non-overlapping events.
     */
    $weekEvents = array(); // output events here
    
    // process each event which is 'day' and 'time'... 
    foreach($events as $newEvent) {
        $newEventIsRunOnce = false; // make testing easier to read later.
    
        // add a 'day' entry to run_once events
        if (!empty($newEvent['date'])) {
            $dt = DateTime::createFromFormat('d M Y', $newEvent['date']);
            $day = $dt->format('l');
            $newEvent = array_merge($newEvent, array('day' => $day));
            $newEventIsRunOnce = true;
        }
    
        // now see if it can be added to the event list...
    
        if (!isset($weekEvents[$newEvent['day']])) { // nothing for today so add
            $weekEvents[$newEvent['day']][] = $newEvent;
            continue; // do the next event...
        }
    
        // now check whether the 'newEvent' overlaps with any events for today in the list.
        // 1) may need to replace the entry with the curEntry with the new one
        // 2) it may overlap more than one entry!
    
        $overlapCount = 0;
        $overlapCurEntryIdx = -1;
        $overlapCurIsRunOnce = false; // makes testing easier to read later
        foreach ($weekEvents[$newEvent['day']] as $idx => $curEvent) {
            if (timeRangeOverlap($curEvent['start'], $curEvent['end'],
                                  $newEvent['start'], $newEvent['end'])) {
               $overlapCount++;
               $overlapCurEntryIdx = $idx;
               $overlapCurIsRunOnce = !empty($curEvent['date']);
            }
        }
    
        // now check to see if overlaps any
        if ($overlapCount === 0) { // ok to add
            $weekEvents[$newEvent['day']][] = $newEvent;
            continue; // do the next event...
        }
    
        // now check to see if overlaps and what type it overlaps with...
        if ($overlapCount === 1) { // only overlaps one event
            if (!$overlapCurIsRunOnce && $newEventIsRunOnce) {
                $weekEvents[$newEvent['day']][$overlapCurEntryIdx] = $newEvent;
            }
            continue; // do the next event...
        }
    }
    
    echo '<pre>';
    print_r($weekEvents);
    

    Time Range Overlap Function:

    /*
     *  need to calculate whether two time ranges overlap.
     *  A time will always be entered as hh:MM in 24 hour format.
     *
     * This is not the most efficient routine but is easy to check that it works
     */
    function timeRangeOverlap($r1Start, $r1End, $r2Start, $r2End)
    {
        // convert all to times that can be compared easily.
        $r1Start = strtotime($r1Start); $r1End = strtotime($r1End);
        $r2Start = strtotime($r2Start); $r2End = strtotime($r2End);
    
        // order them by earliest start time so i can easily see that it works.
        // $r1 will always contain the earliest start time
        if ($r1Start <= $r2Start) {
            $r1 = array('s' => $r1Start, 'e' => $r1End);
            $r2 = array('s' => $r2Start, 'e' => $r2End);
        }
        else {
            $r1 = array('s' => $r2Start, 'e' => $r2End);
            $r2 = array('s' => $r1Start, 'e' => $r1End);
        }
    
        // ensure they do not overlap
        //  in words: r1 ends before r2 starts or r1 starts after r2 ends
        if ($r1['e'] <= $r2['s'] || $r1['s'] >= $r2['e']) {
            return false;
        }
        return true;
    }
    

    Test data:

    $events = Array(
        Array('start' => '20:00', 'end' => '21:00', 'title' => 'event1: run_once',
                'date' => '22 Jun 2014'),
    
        Array('start' => '20:00', 'end' => '22:00', 'title' => 'event1: repeat',
                'day' => 'Sunday'),
    
        Array('start' => '10:00', 'end' => '12:00', 'title' => 'event2 : repeat',
                'day' => 'Sunday'),
    
        Array('start' => '9:00', 'end' => '11:00', 'title' => 'event2 : run_once',
                'date' => '22 Jun 2014'),
    
        Array('start' => '15:00', 'end' => '17:00', 'title' => 'event3 : repeat',
                'day' => 'Sunday'),
    );
    
    点赞 评论 复制链接分享
  • doufu1939 doufu1939 7年前

    If I understand correctly, you want to detect collisions between repeated events and non-repeated events and only display the non-repeated event if it overlaps a repeated event. It doesn't look like your code is performing any checking.

    You should use three arrays. The first contains all the repeated events, the second contains all the non-repeated events for the week at hand. Then you can create a list of 'displayable' events for the week at hand.

    Assuming your two arrays are ordered by starting time, here is a good way to create the displayable array:

    • Compare the starting times of the first element in both arrays, and let E represent the event with the earliest start time.
    • If E is a non-repeated event, add it to the display list.
    • If E is a repeated event, starting with the first element in the non-repeated list, check all the non-repeated events whose start times are before E's ending time. If there are no overlaps, add E to the display list.
    • Take the next earliest event and repeat.

    Once you have your displayable event array, your posted code should work fine.

    点赞 评论 复制链接分享