2010-06-08 61 views
1

使用日期数组(业务开放时间)。我想把它们压缩成他们最简单的形式。PHP:将相似字符串数组精简为一个合并数组

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

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()循环 - 不是很优雅。

谢谢, 马特

编辑:这里是我的代码,到目前为止,产生上述第三阵列(从第一个输入):

$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++; 

} 
+0

您的问题是不明确的。该节目是否应该知道星期二是在星期一之后? “周一至周五”是否为有效套餐?什么是折叠的规则?你应该从那里开始,然后实施将变得更加明显。 – Artefacto 2010-06-08 11:39:15

+0

你能告诉我们你目前使用的功能吗? – Cetra 2010-06-08 11:42:27

+0

嗯,好点。它不一定非常聪明,它应该发现范围,例如:如果星期一,星期二,星期三,星期四和星期五都具有相同的值,那么它应该将第一个和最后一个键(星期一和星期五)连接在一起。虽然你说得对,但“星期二,星期五”也应该是有效的。我会再想一想... – 2010-06-08 11:43:06

回答

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的东西),您将获得两个单独的条目。

+0

你是英雄,谢谢!我会仔细研究一下,看看我能否找到一种方法来修改它,以迎合像你提到的那些边缘案例。感谢您的帮助:) – 2010-06-08 11:57:31

1

我想通过模拟它作为一个关系数据库来解决:

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

那么它很容易降低 - 有特定的时间间隔:

SELECT DISTINCT开始,结束 FROM时间表;

而这些会发生在特定的日子:

SELECT开始,结束,GROUP_CONCAT(天)ORDER BY日分隔符 '' FROM时间表 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 

然后,它是相当简单的工作了连续的日期范围来回在天数列表中。

C.

+0

感谢 - 这些日期已经来自关系数据库,我必须与他们合作,将它们转换为我上面发布的表单(基本上调整日期并在同一天多次组合) 。我可能会考虑在SQL方面做更多的事情,但现在我认为克里斯史密斯的解决方案就是要走的路。 – 2010-06-08 12:18:39

1

作为替代方案,我管理使用array_*功能凑齐版本。尽管如此,'优雅','效率'和'可读性'都被收拾起来了。然而,它确实处理了我在其他答案中提到的边缘案例,并且它给我留下了一个很好的温暖光芒,证明它可以以功能性的方式完成(但同时也是一种耻辱感......))

$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阵列以检查天是否连续。

+0

好吧,现在我很高兴我没有试图自己做这件事 - 我从来没有能力找出所有这些匿名函数和array_ *调用。谢谢! 一个问题:代码给了我这个错误: '解析错误:语法错误,意想不到的T_FUNCTION,预计 ')''上线起点 : 功能($ mydays)使用($天,$ dayIndi​​ces) { 任何想法? – 2010-06-08 12:55:50

+0

@Matt像PHP 5.3.0中介绍的那样,匿名函数就像在代码中溅起的那些函数一样。我想你正在使用较早的版本。如果你真的想这样做,你需要将匿名函数转换成适当的函数(并使用全局变量而不是闭包)。我认为尽管修改其他解决方案会更容易;) – Chris 2010-06-08 13:12:26