【问题标题】:PHP: Condense array of similar strings into one merged arrayPHP:将相似字符串的数组压缩成一个合并数组
【发布时间】:2010-06-08 11:13:35
【问题描述】:

使用一组日期(企业的营业时间)。我想把它们压缩成最简洁的形式。

到目前为止,我是从这个结构开始的

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

}

【问题讨论】:

  • 您的问题定义不明确。程序是否应该知道星期二在星期一之后? “Mon-Tue, Fri”是一个有效的集合吗?折叠的具体规则是什么?你应该从那里开始,也许这样一个实现会变得更加明显。
  • 你能告诉我们你目前使用的功能吗?
  • 嗯,好点。它不必是智能的,它应该只是发现范围,例如:如果 Mon、Tue、Wed、Thu 和 Fri 都具有相同的值,那么它应该将第一个和最后一个键(Mon 和 Fri)连接在一起。虽然你是对的,但“Mon-Tue, Fri”也应该是有效的。我会再考虑一下...
  • Cetra:已编辑以包含我的(凌乱的)当前功能。

标签: php arrays recursion


【解决方案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 的内容,您将获得两个单独的条目)。

【讨论】:

  • 你是英雄,谢谢!我会略读一下,看看我是否能找到一种方法来修改它以适应你提到的那些边缘情况。谢谢你的帮助:)
【解决方案2】:

我会通过将其建模为关系数据库来处理它:

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

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

选择不同的开始,结束 来自时间表;

这些将在特定日期发生:

SELECT 开始、结束、GROUP_CONCAT(day) ORDER BY day SEPERATOR ',' 从时间表 分组开始,结束

(这使用了 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.

【讨论】:

  • 谢谢 - 这些日期已经来自关系数据库,我不得不与他们合作将它们变成我上面发布的表格(基本上调整日期并在同一天多次组合)。我可能会考虑在 SQL 方面做更多的事情,但现在我认为 Chris Smith 的上述解决方案是可行的方法。
【解决方案3】:

作为替代方案,我设法使用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',
 '周二' => '12noon-2:45pm, 5:30pm-10:30pm',
 '星期三' => '12noon-2:45pm, 5:30pm-10:00pm',
 '星期四' => '12noon-2:45pm, 5:30pm-10:30pm',
 '星期五' => '12noon-2:45pm, 5:30pm-10:00pm',
 '星期六' => '12noon-2:45pm, 5:30pm-10:30pm',
 '太阳' => '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 数组来检查日期是否连续。

【讨论】:

  • 好吧,现在我很高兴我没有尝试自己做这件事——我永远不会有能力找出所有这些匿名函数和 array_* 调用。谢谢!一个问题:代码给了我这个错误:Parse error: syntax error, unexpected T_FUNCTION, expecting ')' 在行开始:function ($mydays) use ($days, $dayIndi​​ces) { 有什么想法吗?
  • @Matt Anonymous 函数,如散布在整个代码中的函数,是在 PHP 5.3.0 中引入的。我猜你使用的是早期版本。如果您真的想这样做,则需要将匿名函数拉出到适当的函数中(并使用全局变量而不是闭包)。我认为修改其他解决方案会更容易;)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-30
  • 1970-01-01
  • 2016-07-28
相关资源
最近更新 更多