PHP:将类似字符串的数组压缩为一个合并数组

时间:2010-06-08 11:13:35

标签: php arrays recursion

使用日期数组(业务开放时间)。我想将它们浓缩成最简洁的形式。

到目前为止,我开始使用这种结构

Array
(
    [Mon] => 12noon-2:45pm, 5:30pm-10:30pm
    [Tue] => 12noon-2:45pm, 5:30pm-10:30pm
    [Wed] => 12noon-2:45pm, 5:30pm-10:30pm
    [Thu] => 12noon-2:45pm, 5:30pm-10:30pm
    [Fri] => 12noon-2:45pm, 5:30pm-10:30pm
    [Sat] => 12noon-11pm
    [Sun] => 12noon-9:30pm
)

我想要实现的目标是:

Array
(
    [Mon-Fri] => 12noon-2:45pm, 5:30pm-10:30pm
    [Sat] => 12noon-11pm
    [Sun] => 12noon-9:30pm
)

我已经尝试过写一个递归函数,并设法输出到目前为止:

Array
(
    [Mon-Fri] => 12noon-2:45pm, 5:30pm-10:30pm
    [Tue-Fri] => 12noon-2:45pm, 5:30pm-10:30pm
    [Wed-Fri] => 12noon-2:45pm, 5:30pm-10:30pm
    [Thu-Fri] => 12noon-2:45pm, 5:30pm-10:30pm
    [Sat] => 12noon-11pm
    [Sun] => 12noon-9:30pm
)

有人能看到一种简单的方法来比较这些值并将它们组合在一起吗?我的递归函数基本上是两个嵌套的foreach()循环 - 不是很优雅。

谢谢, 马特

编辑:这是我到目前为止的代码,它产生上面的第3个数组(从第一个数组作为输入):

$last_time = array('t' => '', 'd' => ''); // blank array for looping
$i = 0;

foreach($final_times as $day=>$time) {

    if($last_time['t'] != $time ) { // it's a new time

        if($i != 0) { $print_times[] = $day . ' ' . $time; } 
        // only print if it's not the first, otherwise we get two mondays

    } else { // this day has the same time as last time

        $end_day = $day;

        foreach($final_times as $day2=>$time2) {

            if($time == $time2) {
                $end_day = $day2;
            }

        }

        $print_times[] = $last_time['d'] . '-' . $end_day . ' ' . $time;

    }

$last_time = array('t' => $time, 'd' => $day);
$i++;

}

3 个答案:

答案 0 :(得分:1)

我认为没有一个特别优雅的解决方案。经过多次试验内置的array_*函数试图找到一个很好的简单解决方案后,我放弃了并想出了这个:

$lastStart = $last = $lastDay = null;
$new = array();

foreach ($arr as $day => $times) {
 if ($times != $last) {
  if ($last != null) {
   $key = $lastStart == $lastDay ? $lastDay : $lastStart . '-' . $lastDay;
   $new[$key] = $last;
  }
  $lastStart = $day;
  $last = $times;
 }
 $lastDay = $day;
}

$key = $lastStart == $lastDay ? $lastDay : $lastStart . '-' . $lastDay;
$new[$key] = $last;

它只使用一个foreach循环而不是你的两个,因为它保持一堆状态。它只会将相邻的日子合并在一起(例如,如果星期三改变,你就不会得到像Mon-Tue,Thu-Fri这样的东西,你会得到两个单独的条目。)

答案 1 :(得分:1)

我通过将其建模为关系数据库来接近它:

day      start        end
1        12:00        14:45
1        17:30        22:30
...

然后它很容易减少 - 有特定的时间间隔:

SELECT DISTINCT start,end    从时间表开始;

这些将在特定日期发生:

SELECT start,end,GROUP_CONCAT(day)ORDER BY day SEPERATOR','    从时间表    GROUP BY开始,结束

(这使用仅限MySQL的'group_concat'函数 - 但该方法在无法使用的情况下相同) 会给:

12:00    14:45  1,2,3,4,5
17:30    22:30  1,2,3,4,5
12:00    23:00  6
12:00    21:30  7

然后从日期列表中计算出连续的日期范围相当简单。

下进行。

答案 2 :(得分:1)

作为替代方案,我设法使用array_*函数拼凑了一个版本。但在某些时候,“优雅”,“效率”和“可读性”都被打包并离开了。然而,它确实处理了我在另一个答案中提到的边缘情况,它给我留下了一个温暖的光芒,证明它可以以一种功能性的方式完成(但同时又感到羞耻......)< / p>

$days = array_keys($arr);
$dayIndices = array_flip($days);

var_dump(array_flip(array_map(
   function ($mydays) use($days, $dayIndices) {
       return array_reduce($mydays,
           function($l, $r) use($days, $dayIndices) {
               if ($l == '') { return $r; }
               if (substr($l, -3) == $days[$dayIndices[$r] - 1]) {
                   return ((strlen($l) > 3 && substr($l, -4, 1) == '-') ? substr($l, 0, -3) : $l) . '-' . $r;
               }
               return $l . ',' . $r;
           }, '');
   }, array_map(
       function ($day) use ($arr) {
          return array_keys($arr, $arr[$day]);
       }, array_flip($arr)
   )
)));

我用这个输入测试了它:

 'Mon' => '12noon-2:45pm, 5:30pm-10:30pm',
 'Tue' => '12noon-2:45pm, 5:30pm-10:30pm',
 'Wed' => '12noon-2:45pm, 5:30pm-10:00pm',
 'Thu' => '12noon-2:45pm, 5:30pm-10:30pm',
 'Fri' => '12noon-2:45pm, 5:30pm-10:00pm',
 'Sat' => '12noon-2:45pm, 5:30pm-10:30pm',
 'Sun' => '12noon-9:30pm'

得到了这个:

  ["Mon-Tue,Thu,Sat"]=> string(29) "12noon-2:45pm, 5:30pm-10:30pm"
  ["Wed,Fri"]=> string(29) "12noon-2:45pm, 5:30pm-10:00pm"
  ["Sun"]=> string(13) "12noon-9:30pm"

基本上,最后的array_map会将输入转换为关联数组,这些数组会出现在它们出现的天数数组中。之前的大块代码使用array_reduce将这些天数缩减为格式良好的字符串,咨询$days$dayIndices数组以检查天数是否连续。