在具有数值的数组中,选择范围内的连续值并将它们全部设置为它们的最高值

All of that in an associative array.

I have an array that holds as keys some IDs and as values some integers:

array([24]=>1620, [49]=>1620, [35]=>1622, [101]=>1623, [214]=>1630, [50]=>1638, [5]=>1640)

What I want to obtain is, while "LastValue - FirstValue <= 4", to set all near values to the highest of them, like this:

array([24]=>1623, [49]=>1623, [35]=>1623, [101]=>1623, [214]=>1630, [50]=>1640, [5]=>1640)

In reality is about some events which have a planned date; that planned date is stored as week number; I want to group the successive individual events within a month to the last week in that group.

So I have created this array with the IDs of events and the dates; the array is sorted ascendant by values. But I can't figured out how to group the successive individual values within a month, set them to the highest of them (last one) then pass to the next values, identify the group, set the values, etc

Any suggestions? Thanks!

The method you describe is ambiguous when you have long series of weeks without gaps. In that case there are several ways to group weeks together keeping the condition that the first and last week of the group should not be more than 4 weeks apart.

Assuming you don't expect such long series of non-interrupted week numbers, I present here an algorithm that goes backwards through the (sorted) array of events. It temporarily transforms week numbers to "absolute" week numbers, which do not reset to 1 each year, but keep increasing with one. That way the algorithm can equally find groups that cross year boundaries:

// Sample data
$data = array(24=>1620, 49=>1620, 35=>1622, 101=>1623, 214=>1630, 50=>1638, 5=>1640);

$last_abs_week = 99999;
$date = new DateTime();
// first week in the "absolute" week numbering:
$ref_date = new DateTime();
$ref_date->setISODate(2000, 1);
// Loop backwards:
foreach (array_reverse(array_keys($data)) as $event) {
    $week = $data[$event];
    // Get date corresponding to week number
    $date->setISODate("20" .  substr($week, 0, 2),  substr($week, 2));
    // Convert date to number of weeks since year 2000 to allow week numbers
    // to be subtracted even when belonging to different years
    $abs_week = $date->diff($ref_date)->format("%a") / 7;
    if ($abs_week < $last_abs_week - 4) {
        // Not within same group: start a new group
        $last_week = $week;
        $last_abs_week = $abs_week;
    } else {
        // In same group: assign last week of group
        $data[$event] = $last_week;
    }
}

print_r ($data);

Output for the sample data is:

Array
(
    [24] => 1623
    [49] => 1623
    [35] => 1623
    [101] => 1623
    [214] => 1630
    [50] => 1640
    [5] => 1640
)

Forward search

Here is an alternative way, where the array is scanned in the given order:

$first_abs_week = 0;
$group = [];
$date = new DateTime();
$ref_date = new DateTime();
$ref_date->setISODate(2000, 1);
foreach ($data as $event => $week) {
    // Get date corresponding to week number
    $date->setISODate("20" .  substr($week, 0, 2),  substr($week, 2));
    // Convert date to number of weeks since year 2000 to allow week numbers
    // to be subtracted even when belonging to different years
    $abs_week = $date->diff($ref_date)->format("%a") / 7;
    if (count($group) && $abs_week > $first_abs_week + 4) {
        $week = $data[array_pop($group)];
        foreach ($group as $prev_event) {
            $data[$prev_event] = $week;
        }
        $group = [];
        $first_abs_week = $abs_week;
    }
    $group[] = $event;
}

print_r ($data);

Output for the sample data is the same as for the first alternative.