仅当日期值连续时才按多列对二维数组的行进行分组

问题描述 投票:0回答:6

如果除

startdate
之外的所有值都相同,我想要合并多个关联数组。如果两个数组确实相同,我想合并它们并创建一个新元素
enddate
,以便
startdate
enddate
显示日期范围。该范围内的所有日期都必须在原始数组中表示,即如果缺少日期,则不得合并其两侧的日期。

Array('color'=>'red','size'=>'large','shape'=>'circle','startdate'=>'2011-08-17')
Array('color'=>'red','size'=>'large','shape'=>'circle','startdate'=>'2011-08-18')
Array('color'=>'red','size'=>'large','shape'=>'square','startdate'=>'2011-08-20')

应该变成:

Array('color'=>'red','size'=>'large','shape'=>'circle','startdate'=>'2011-08-17','enddate'=>'2011-08-18')
Array('color'=>'red','size'=>'large','shape'=>'square','startdate'=>'2011-08-20')

到目前为止,我已经尝试循环每个数组并创建一个多维数组:

foreach($arrays as $id => $array){
    $mergearray[$array['red']][$array['large']][$array['circle']] = $id;
}

为了检查另一个数组是否具有相同的值。我正在尝试使用这些数组来重建原始结构中的数组。

php arrays date multidimensional-array grouping
6个回答
1
投票

避免使用日期/日期函数,这会浪费 CPU 时间,而且没有任何额外的好处(Dave 的回答)。 避免大量使用数组函数(adlawson 的回答),同样浪费时间,仅对数组进行排序并以智能方式处理会快得多。

但最重要的是,你想做什么,为什么这些数组是这样的,以及......这一切背后是否有 SQL?

因为如果有的话,有比尝试合并格式错误的数组更简单的解决方案(恕我直言,您寻求的转换意味着在某个时刻出了问题。start_time 变成 end_time 表示尝试保持完整的时间)项目历史记录,在 PHP 本身中没有位置)。

如果确实是您需要的,我会给您正确的 PHP 代码,但我很怀疑这是继续您的应用程序的正确方法。


0
投票

这样的事情应该可以解决问题:

$newArray = array();
$newLine = false;
$prev_day = false;
foreach ($array as $line) {
    $this_day = strtotime($line['startdate']);
    if (($newLine) && ($prev_day == strtotime('-1 day', $this_day))) {
        $newLine['enddate'] = $line['startdate'];
        $newArray[] = $newLine;
        $newLine = false;
    } elseif (!$newLine) {
        $newLine = $line;
    }
    if ($newLine) {
        $newLine['enddate'] = $line['startdate'];
    }
    $prev_day = $this_day;
}
$newArray[] = $newLine;

0
投票

我看不到这种合并的实际实现,但这应该可行。它可能看起来很冗长,但它会处理一些事情。

/**
 * @example array_merge_allbutstartdate($array1, $array2, $array3, $array4, $arr...)
 * @param array $array1
 * @param array $array2, $arr...
 * @return array
 */
function array_merge_allbutstartdate(array $array1, array $array2)
{
    $out  = array();
    $date = array();
    $startKey = 'startdate';
    $endKey   = 'enddate';

    foreach (func_get_args() as $item) {
        if (!is_array($item)) {
            trigger_error('All arguments should be an array.', E_USER_ERROR);
        }

        $temp = null;
        if (isset($item[$startKey])) {
            $temp = $item[$startKey];
            unset($item[$startKey]);
        }

        if (!in_array($item, $out)) {
            $i = count($out);
            $out[] = $item;
        } else {
            $i = array_search($item, $out);
        }

        if (null !== $temp) {
            $date[$i][] = $temp;
        }
    }

    foreach ($date as $j => $row) {
        array_map('strtotime', $row);
        $start = array_search(min($row), $row);
        $end = array_search(max($row), $row);

        // Add start date
        $out[$j][$startKey] = $date[$j][$start];

        // Only add end date if it is not equal to start date
        if ($date[$j][$start] !== $date[$j][$end]) {
            $out[$j][$endKey] = $date[$j][$end];
        }
   }

    return $out;
}

0
投票

鉴于您已经有一个数组数组,您实际上要做的是删除连续条目(每个时间跨度的第一个条目除外)。

你的算法是:

$expected_start_time= 0; // initial val
foreach($all_entries as $k => &$v) {
    $start_time = strtotime($v['startdate']);
    if($start_time != $expected_start_time) {
        $range_start =& $v; // this is a range beginning. Put end date in here
    } else {
        $range_start['enddate'] = $v['startdate'];
        unset($all_entries[$k]);
    }
    $expected_date = strtotime('+1 day', $start_time);
}

这基本上是 Dave Child 答案的更简单、更到位的版本。


0
投票
function group_and_sort($data)
{
    $out = array();
    while($data) {

        // Shift off the first element
        $buffer = array_shift($data);
        $end_date = $buffer['startdate'];

        // Try to group successive elements...
        while($data) {

            // Case 1: Does the next element differ by more than just date?
            if(count(array_diff_assoc($buffer, $data[0])) > 1) {
                break;
            }

            // Case 2: Does the next element have an unexpected date?
            $expected_date = date('Y-m-d', strtotime('+1 day', strtotime($end_date)));
            if($data[0]['startdate'] != $expected_date) {
                break;
            }

            // Otherwise, push end_date forward and throw away the element
            $end_date = $data[0]['startdate'];
            array_shift($data);
        }

        // If the dates differ, record the range.
        if($buffer['startdate'] != $end_date) {
            $buffer['enddate'] = $end_date;
        }

        $out[] = $buffer;
    }

    return $out;
}

假设元素已按日期排序。如果不是,您可以使用:

function sort_startdate($a, $b)
{
    return strcmp($a['startdate'], $b['startdate']);
}
usort($data, 'sort_startdate');

在将其传递给

group_and_sort($data)
之前。


0
投票

确保您的输入数据至少按

startdate
值排序。

迭代行,并使用由 3 个标识列构建的字符串来确定分组。

如果第一次遇到某个组,或者当前行没有立即跟随专用组的最后一个日期,则开始一个新组。

如果多次遇到某个组并且有连续的日期,则将该新日期写为新的

enddate

使用引用可以防止需要跟踪先前的值已被推入结果数组的位置。 演示

usort($array, fn($a, $b) => $a['startdate'] <=> $b['startdate']);
$result = [];
foreach ($array as $row) {
    $compositeKey = implode(
        '_',
        [$row['color'], $row['size'], $row['shape']]
    );
    if (
        !isset($ref[$compositeKey])
        || ($row['startdate'] != date('Y-m-d', strtotime(($ref[$compositeKey]['enddate'] ?? $ref[$compositeKey]['startdate']) . ' + 1 day')))
    ) {
        if (isset($ref[$compositeKey])) {
           unset($ref[$compositeKey]);
        }
        $ref[$compositeKey] = $row;
        $result[] =& $ref[$compositeKey];
    } else {
        $ref[$compositeKey]['enddate'] = $row['startdate'];
    }
}
var_export($result);
© www.soinside.com 2019 - 2024. All rights reserved.