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

发布于 2024-09-05 11:31:06 字数 1730 浏览 2 评论 0原文

使用日期数组(企业的营业时间)。我想将它们压缩成尽可能简短的形式。

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

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

}

Working with an array of dates (opening times for a business). I want to condense them to their briefest possible form.

So far, I started out with this structure

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
)

What I want to achieve is this:

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

I've tried writing a recursive function and have managed to output this so far:

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
)

Can anybody see a simple way of comparing the values and combining the keys where they're similar? My recursive function is basically two nested foreach() loops - not very elegant.

Thanks,
Matt

EDIT: Here's my code so far, which produces the 3rd array above (from the first one as input):

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

}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

相权↑美人 2024-09-12 11:31:06

我认为对此没有特别优雅的解决方案。在对内置 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 的内容,您将获得两个单独的条目)。

I don't think there is a particularly elegant solution to this. After much experimenting with the built in array_* functions trying to find a nice simple solution, I gave up and came up with this:

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

It only uses one foreach loop as opposed to your two, as it keeps a bunch of state. It'll only merge adjacent days together (i.e., you won't get something like Mon-Tue,Thu-Fri if Wednesday is changed, you'll get two separate entries).

怼怹恏 2024-09-12 11:31:06

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

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 start,end

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

I'd approach it by modelling it as a relational database:

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

Then its fairly easy to reduce - there are specific time intervals:

SELECT DISTINCT start, end
FROM timetable;

And these will occur on specific days:

SELECT start, end, GROUP_CONCAT(day) ORDER BY day SEPERATOR ','
FROM timetable
GROUP BY start,end

(this uses the MySQL-only 'group_concat' function - but the method is the same where this is not available)
would give:

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

Then it's fairly simple to work out consecutive date ranges from the list of days.

C.

何其悲哀 2024-09-12 11:31:06

作为替代方案,我设法使用 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$dayIndi​​ces 数组来检查是否天是否连续。

As an alternative, I managed to cobble together a version using array_* functions. At some point though, 'elegance', 'efficiency' and 'readability' all packed up and left. It does, however, handle the edge cases I mentioned in the other answer, and it left me with a nice warm glow for proving it could be done in a functional manner (yet at the same time a sense of shame...)

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

I tested it with this input:

 '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'

And got this:

  ["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"

Basically, the array_map at the end transforms the input into an associative array of times to an array of days that they occur on. The large block of code before that reduces those days into a nicely formatted string using array_reduce, consulting the $days and $dayIndices arrays to check if days are consecutive or not.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文