如果除
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;
}
为了检查另一个数组是否具有相同的值。我正在尝试使用这些数组来重建原始结构中的数组。
避免使用日期/日期函数,这会浪费 CPU 时间,而且没有任何额外的好处(Dave 的回答)。 避免大量使用数组函数(adlawson 的回答),同样浪费时间,仅对数组进行排序并以智能方式处理会快得多。
但最重要的是,你想做什么,为什么这些数组是这样的,以及......这一切背后是否有 SQL?
因为如果有的话,有比尝试合并格式错误的数组更简单的解决方案(恕我直言,您寻求的转换意味着在某个时刻出了问题。start_time 变成 end_time 表示尝试保持完整的时间)项目历史记录,在 PHP 本身中没有位置)。
如果确实是您需要的,我会给您正确的 PHP 代码,但我很怀疑这是继续您的应用程序的正确方法。
这样的事情应该可以解决问题:
$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;
我看不到这种合并的实际实现,但这应该可行。它可能看起来很冗长,但它会处理一些事情。
/**
* @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;
}
鉴于您已经有一个数组数组,您实际上要做的是删除连续条目(每个时间跨度的第一个条目除外)。
你的算法是:
$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 答案的更简单、更到位的版本。
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)
之前。
确保您的输入数据至少按
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);