【问题标题】:Displaying a sequence of days显示几天的序列
【发布时间】:2014-07-19 15:32:38
【问题描述】:

假设我有这些数字数组,它们对应于一周中的天数(从星期一开始):

/* Monday - Sunday */
array(1, 2, 3, 4, 5, 6, 7)

/* Wednesday */
array(3)

/* Monday - Wednesday and Sunday */
array(1, 2, 3, 7)

/* Monday - Wednesday, Friday and Sunday */
array(1, 2, 3, 5, 7)

/* Monday - Wednesday and Friday - Sunday */
array(1, 2, 3, 5, 6, 7)

/* Wednesday and Sunday */
array(3, 7)

如何有效地将这些数组转换为 C 样式 cmets 中所示的所需字符串?任何帮助将不胜感激。

【问题讨论】:

  • 它们会一直井井有条吗?
  • 是的。如果没有,它们可以被排序。
  • 有趣的问题,但如果您可以为您的尝试提供问题,那么在 PHP 标记中(也可能在网站的其他地方)中表示赞赏。没有尝试的问题 +3 出奇的高 - 这些有时会吸引反对票,所以你知道:)
  • @Félix,这当然是一个有趣的挑战。尽管如此,IMO 的所有发帖者都应该首先意识到提供代码的想法——如果只是为了“陷入困境”并让自己成为更好的程序员。我认为,这里有一个共识,即自己尝试比提供高质量的答案更具教育意义。
  • @halfer:同意,我应该尝试自己解决这个问题,也许会问我如何改进该代码。我想我还是会试一试,因为我想在编码方面做得更好。

标签: php date days


【解决方案1】:

以下代码应该可以工作:

<?php
// Create a function which will take the array as its argument
function describe_days($arr){
$days = array("Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday");
// Begin with a blank string and keep adding data to it
$str = "";
// Loop through the values of the array but the keys will be important as well
foreach($arr as $key => $val){
// If it’s the first element of the array or ...
// an element which is not exactly 1 greater than its previous element ...
    if($key == 0 || $val != $arr[$key-1]+1){
        $str .= $days[$val-1]."-";
    }
// If it’s the last element of the array or ...
// an element which is not exactly 1 less than its next element ...
    if($key == sizeof($arr)-1){
        $str .= $days[$val-1];
    }
    else if($arr[$key+1] != $val+1){
        $str .= $days[$val-1]." and ";
    }
}
// Correct instances of repetition, if any
$str = preg_replace("/([A-Z][a-z]+)-\\1/", "\\1", $str);
// Replace all the "and"s with commas, except for the last one
$str = preg_replace("/ and/", ",", $str, substr_count($str, " and")-1);
return $str;
}

var_dump(describe_days(array(4, 5, 6)));      // Thursday-Saturday
var_dump(describe_days(array(2, 3, 4, 7)));   // Tuesday-Thursday and Sunday
var_dump(describe_days(array(3, 6)));         // Wednesday and Saturday
var_dump(describe_days(array(1, 3, 5, 6)));   // Monday, Wednesday and Friday-Saturday
?>

【讨论】:

  • 您的示例没有给出“周一 - 周三和周五”,而是给出了“周一 - 周五”。
  • 我喜欢你的解决方案,似乎没有我的那么“痛苦”。但是你能评论一下吗?我没有得到一切。 (我目前正在查看它以了解所做的事情,但如果已经有一些信息会更容易。:))
  • 它不适用于 1,3,5,7。它给出的结果是:周一至周三和周五和周日,应该是周一和周三以及周五和周日。
  • 2,4,6 相同。它给出了周二至周四和周六,而不是周二、周四和周六。
【解决方案2】:
/* Monday - Sunday */
$days1 = [1, 2, 3, 4, 5, 6, 7];

/* Monday - Wednesday and Sunday */
$days2 = [1, 2, 3, 7];

/* Wednesday and Sunday */
$days3 = [3, 7];

/* Monday - Wednesday and Friday - Sunday */
$days4 = [1, 2, 3, 5, 6, 7];

/* Monday - Wednesday, Friday and Sunday */
$days5 = [1, 2, 3, 5, 7];

/* Monday, Wednesday, Friday and Sunday */
$days6 = [1, 3, 5, 7];

// creates logic groups out of flat array
function splitIntoSequences(array $days)
{
    $collection = [];
    do {
        $seq = extractSequence($days);
        $collection[] = $seq;

        $days = array_diff($days, $seq);
    }
    while (!empty($days));

    return $collection;
}

// filters the next sequence
function extractSequence(array $days)
{
    sort($days);

    $seq = [];
    foreach ($days as $day) {
        // seq break
        if (!empty($seq) && $day - 1 != $seq[count($seq) - 1]) {
            return $seq;
        }

        $seq[] = $day;
    }

    return $seq;
}

// removes all unneeded steps from sequences
function cleanupSequences(array $collection)
{
    $cleanedCollection = [];
    foreach ($collection AS $seq) {
        if (count($seq) == 1) {
            $cleaned = [array_pop($seq)];
        } else {
            $cleaned = [array_shift($seq), array_pop($seq)];
        }

        $cleanedCollection[] = $cleaned;
    }

    return $cleanedCollection;
}

// convert whole collection to daynames
function convertToDaynames(array $collection)
{
    $daynameCollection = [];
    foreach ($collection AS $seq) {
        $days = [];
        foreach ($seq as $day) {
            $days[] = numberToDay($day);
        }

        $daynameCollection[] = $days;
    }

    return $daynameCollection;
}

// number to dayname
function numberToDay($weekday)
{
    $relative = sprintf('next Sunday + %d day', $weekday);
    $date = new \DateTime($relative);

    return $date->format('l');
}

// format output
function format(array $collection)
{
    $t = [];
    foreach ($collection as $seq) {
        $t[] = implode(' - ', $seq);
    }

    if (count($t) == 1) {
        return array_pop($t);
    }

    $last = array_pop($t);

    return sprintf('%s and %s', implode(', ', $t), $last);
}

测试调用:

function makeMeHappy(array $seq)
{
    $splitted = splitIntoSequences($seq);
    $cleaned = cleanupSequences($splitted);
    $daynames = convertToDaynames($cleaned);
    return format($daynames);
}


var_dump(makeMeHappy($days1));
// string(15) "Monday - Sunday"

var_dump(makeMeHappy($days2));
// string(29) "Monday - Wednesday and Sunday"

var_dump(makeMeHappy($days3));
// string(20) "Wednesday and Sunday"

var_dump(makeMeHappy($days4));
// string(38) "Monday - Wednesday and Friday - Sunday"

var_dump(makeMeHappy($days5));
// string(37) "Monday - Wednesday, Friday and Sunday"

var_dump(makeMeHappy($days6));
// string(36) "Monday, Wednesday, Friday and Sunday"

【讨论】:

    【解决方案3】:

    我们可以将数组问题转换为字符串问题,并使用正则表达式 (regex) 解决方案...它通过 finite automata 间接检查所有条件和案例。

    PS:当我们的数组短,case 数量少时,这种算法构造是安全的,并且提供了完整的解决方案。

    哦,是的,我们还可以将周数翻译成其他语言(参见 $lang 参数)!

    多语言和正则表达式解决方案

    function weekNumbers_toStr($days, $lang='en') {
         $and = array('pt'=>'e', 'en'=>'and');
         $strWeek = array(     // config with more langs!
            'pt'=>array("Segunda", "Terça", "Quarta", "Quinta", "Sexta", 
                      "Sábado", "Domingo"),
            'en'=> array("Monday", "Tuesday", "Wednesday", "Thursday", 
                      "Friday", "Saturday", "Sunday")
         );
         $days = array_unique($days);
         sort($days);
         $seq = preg_replace_callback(  // Split sequence by ",":
            '/0246|024|025|026|135|146|246|02|03|04|05|06|13|14|15|16|24|25|26|35|36|46/',
            function ($m) { return join( ',' , str_split($m[0]) ); },
            join('',$days)
         );
         // split two or more days by "-":
         $seq = preg_replace('/(\d)\d*(\d)/', '$1-$2', $seq);
         $a = explode(',',$seq);
         $last = array_pop($a);
         $n = count($a); 
         // Formating and translating:
         $seq = $n? join(", ",$a): $last;
         if ($last && $n) $seq = "$seq $and[$lang] $last";
         return preg_replace_callback(
            '/\d/',
            function ($m) use (&$strWeek,$lang) { 
               return $strWeek[$lang][$m[0]]; 
            },
            $seq
         );
     } // func
    

    测试:

    print "\n".weekNumbers_toStr(array(6,1,2,3,6),'en'); // corrects order and dups
    print "\n".weekNumbers_toStr(array(0,1,2,3,6));      // Monday-Thursday and Sunday
    
    print "\n".weekNumbers_toStr(array(3,4,6),'pt');  // Quinta-Sexta e Domingo
    print "\n".weekNumbers_toStr(array(3,4,6));       // Thursday-Friday and Sunday
    
    print "\n".weekNumbers_toStr(array(2,3,4,6));  // Wednesday-Friday and Sunday
    print "\n".weekNumbers_toStr(array(3,5));      // Thursday and Saturday
    print "\n".weekNumbers_toStr(array(0,2,4,6));  // Monday, Wednesday, Friday and Sunday
    print "\n".weekNumbers_toStr(array(0));        // Monday
    print "\n".weekNumbers_toStr(array());         // (nothing)
    

    【讨论】:

    • 我必须安静地阅读它以了解您的代码在做什么。与我不同的可视化方式!正如你所说,比我的更优雅。就执行时间/服务器资源而言,它是否也更有效?问题是,我发现我的答案更容易理解 - 当然对我来说 - 我不想说我发现我的更好(一点也不!),事实上我想知道你的答案是否更好以及为什么。适合多语言。当你说安全时,我的不是吗?如果是,为什么?真正的问题,我想了解和提高我的 PHP 技能,我不是在这里说“嗯,我的代码很好”。 :)
    • @caCtus,对不起,我编辑了,现在你可以测试了。就像一个解析器......你的算法是好的(!),这只是一个异国情调,以说明“正则表达式解决方案”。也许性能最差(你能做一个基准测试吗?),是的,“可以理解”:只有在“序列中断”和“两天或更长时间”的展示案例中更好(参见 cmets)。
    • 感谢所有这些解释!我对正则表达式不是很熟悉(我只在很明显需要它们时才使用它,例如当我必须检查电子邮件地址时)所以这个解决方案(以及@SharanyaDutta's)对我来说非常有趣。 :)
    • 关于性能。基准测试时,CPU 时间几乎相同,@caCtus 的displayDays() 更快。使用 displayDays 作为 CPU 时间单位(越多越好),我们有:my weekNumbers_toStr()=2.4; @SharanyaDutta 的 describe_days()=1.3。 @Otanaught 的 makeMeHappy()displayDays 慢 10 倍。 My 是稳定的,describe_days()displayDays() 在输入元素较少时更快。 PS:和我的比较,需要去掉多语言,sortarray_unique行。
    • @caCtus,不客气。现在,阅读所有内容,我认为@SharanyaDutta 更“易于理解” ;-) 当使用 jddayofweek() 几乎同时运行时,1.17 displayDays
    【解决方案4】:

    我试过并测试了这个:

    /* Monday - Sunday */
    $days1 = array(1, 2, 3, 4, 5, 6, 7);
    
    /* Monday - Wednesday and Sunday */
    $days2 = array(1, 2, 3, 7);
    
    /* Wednesday and Sunday */
    $days3 = array(3, 7);
    
    /* Monday - Wednesday and Friday - Sunday */
    $days4 = array(1, 2, 3, 5, 6, 7);
    
    /* Monday - Wednesday, Friday and Sunday */
    $days5 = array(1, 2, 3, 5, 7);
    
    /* Monday, Wednesday, Friday and Sunday */
    $days6 = array(1, 3, 5, 7);
    
    function displayDays($days = array()) {
    
        // 1: Create periods and group them in arrays with starting and ending days
        $periods = array();
    
        $periodIndex = 0;
    
        $previousDay = -1;
        $nextDay = -1;
    
        foreach($days as $placeInList => $currentDay) {     
            // If previous day and next day (in $days list) exist, get them.
            if ($placeInList > 0) {
                $previousDay = $days[$placeInList-1];
            }
            if ($placeInList < sizeof($days)-1) {
                $nextDay = $days[$placeInList+1];
            }
    
            if ($currentDay-1 != $previousDay) {
            // Doesn't follow directly (in week) previous day seen (in our list) = starting a new period
                $periodIndex++;
                $periods[$periodIndex] = array($currentDay);
            } elseif ($currentDay+1 != $nextDay) {
            // Follows directly previous day, and isn't followed directly (in week) by next day (in our list) = ending the period       
                $periods[$periodIndex][] = $currentDay;
                $periodIndex++;
            }
        }
        $periods = array_values($periods);
    
    
        // Arrived here, your days are grouped differently in bidimentional array.
        // print_r($periods); // If you want to see the new array's structure
    
        // 2: Display periods as we want.
        $text = '';
        foreach($periods as $key => $period) {
            if ($key > 0) {
            // Not first period
                if ($key < sizeof($periods)-1) {
                // Not last period either
                    $text .= ', ';
                } else {
                // Last period
                    $text .= ' and ';
                }
            }
    
            if (!empty($period[1])) {
            // Period has starting and ending days
                $text .= jddayofweek($period[0]-1, 1).' - '.jddayofweek($period[1]-1, 1);
            } else {
            // Period consists in only one day
                $text .= jddayofweek($period[0]-1, 1);
            }
        }
    
        echo $text.'<br />';
    }
    
    displayDays($days1);
    displayDays($days2);
    displayDays($days3);
    displayDays($days4);
    displayDays($days5);
    displayDays($days6);
    

    jddayofweek() 返回星期几。在那个函数中,0 是星期一,6 是星期天,这就是为什么我每次使用它时这里的“-1”:你的星期一是 1,你的星期日是 7。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-11-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多