#php #days
#php #диапазон #синтаксический анализ текста #дни #перенос
Вопрос:
Я пытаюсь сжать выражение отдельных дней в более короткое выражение, включающее диапазоны, разделенные дефисом.
Примеры:
mon,tue,wed,thu,fri,sat
чтобы быть:mon-sat
mon,tue,wed,fri,sat
чтобы бытьmon-wed,fri-sat
Моя попытка кодирования:
function dayrange($days){
$days = explode(",", str_replace(" ","",$days));
return reset($days) . "-" . end($days);
}
Как я могу сократить многодневное выражение, чтобы последовательные дни были объединены в диапазон дней?
Комментарии:
1. Нужно ли переносить и на неделю? ie. если вводятся данные типа пт, сб, вс, пн, вы ожидаете, что будет пт-пн или пт-вс, вс-пн?
2. хм, перенос недели не требуется. было бы неплохо иметь, я полагаю, но не нужно для этого проекта
Ответ №1:
В принципе, я бы подошел к этому следующим образом:
- Преобразование дней в соответствующие числовые значения
- Преобразование массива чисел в строку с диапазонами
- Преобразование чисел в строке обратно в дни недели
Я написал некоторый код для этого:
/**
* Convert an array of numbers to a string containing ranges and single values
* @param array $numbers an array of numbers
* @return string
*/
function compressNumbers($numbers) {
$result = array();
sort($numbers);
$previousValue = reset($numbers);
$startValue = $previousValue;
foreach ($numbers as $value) {
if ($value > $previousValue 1) {
if ($startValue == $previousValue) {
$result[] = $startValue;
} else {
$result[] = $startValue . '-' . $previousValue;
}
$startValue = $value;
}
$previousValue = $value;
}
if ($startValue == $previousValue) {
$result[] = $startValue;
} else {
$result[] = $startValue . '-' . $previousValue;
}
return implode(',', $result);
}
/*
* Creates an array with values the three letter representation for days of the
* week and keys the corresponding numeric representation.
*
* @return array
*/
function createLookupNumberToDay() {
$date = strtotime('now');
$lookup = array();
for ($i = 1; $i <= 7; $i ) {
$lookup[date('w', $date)] = date('D', $date);
$date = strtotime(' 1 day', $date);
}
return $lookup;
}
/*
* Converts a string listing days separated by commas into
* an array with values the numeric value for the corresponding
* day of the week.
*
* @param string $days
* @return array
*/
function convertDaysToNumbers($days) {
$result = array();
$daysArray = explode(",", str_replace(" ","",$days));
foreach ($daysArray as $day) {
$result[] = date('w', strtotime($day));
}
return $result;
}
/*
* Converts the numbers in a string to the corresponding 3-letter day of the
* week abbreviation.
*
* @param string $string
* @return string
*/
function convertNumbersToDays($string) {
$lookup = createLookupNumberToDay();
return str_replace(array_keys($lookup), $lookup, $string);
}
function convert($string) {
return (convertNumbersToDays(compressNumbers(convertDaysToNumbers($string))));
}
echo convert('mon,tue,wed,thu,fri,sat');
echo '<br />';
echo convert('mon,tue,wed,sat');
echo '<br />';
Ответ №2:
Не тестировал это, но должно дать вам хорошее начало. Он также обрабатывает перенос недели.
function dayrange($days){
$wdays = array("mon","tue","wed","thu","fri","sat","sun");
$indays = explode(",", str_replace(" ","",$days)); // expand the list to an array
$retstr = array_shift($indays); // get the first date
$curpos = array_search($retstr, $wdays); // current position in the wdays array
$intv = 0; // interval between days to avoid mon-tue like output
foreach($indays as $d) {
if($d == $wdays[$curpos]) {
$curpos = ($curpos ) % 7; // this will take care of wrapping.
$intv ;
} else {
$retstr.= ($intv > 1 ? "-".$d:",".$d); // use appropriate join
$intv = 0; // reset interval
}
}
if($intv > 0) { // if anything was left deal with the end.
$retstr.= ($intv > 1 ? "-".$d:",".$d);
} else {
$retstr.= ",".$d;
}
return ($retstr);
}
Комментарии:
1. codepad.org/UKCMaRLN … только что вернул ту же строку, которую я ей дал? 1 за усилия
2. @Jason a sympathy uv? Это не помогает исследователям находить хорошие, работающие решения. Напротив, ответы, которые не работают, должны иметь отрицательную оценку.
3. Действительно, этот ответ не возвращает желаемый результат .
Ответ №3:
-
Определите поиск как константу, чтобы легко определить последовательное расположение каждого дня.
-
Разбейте строку на запятые и повторите значения дня.
-
Если результирующая строка пуста, добавьте день без разделителя / склейки.
-
Если день является последовательно расположенным днем, то потенциально удалите добавленную подстроку yesterday, если она была присоединена с использованием дефиса, затем добавьте дефис и день.
-
Если день не является последовательно расположенным днем, тогда добавьте запятую и день.
Код: (демо)
define('DAYS', array_flip(['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun']));
function condenseDays(string $days): string
{
$result = '';
foreach (explode(',', $days) as $day) {
if (!$result) {
$result .= $day;
} elseif (DAYS[$day] === DAYS[$yesterday] 1) {
$result = str_replace("-$yesterday", '', $result) . "-$day";
} else {
$result .= ",$day";
}
$yesterday = $day;
}
return $result;
}
echo condenseDays('mon,tue,wed,thu,fri,sat') . "n";
echo condenseDays('tue,thu,fri,sun') . "n";
echo condenseDays('mon,tue,wed,fri,sat,sun') . "n";
echo condenseDays('mon,thu,sun') . "n";
echo condenseDays('tue,wed,fri,sat') . "n";
echo condenseDays('mon,wed,fri,sun') . "n";
echo condenseDays('mon,tue,thu,fri,sat,sun');
Вывод:
mon-sat
tue,thu-fri,sun
mon-wed,fri-sun
mon,thu,sun
tue-wed,fri-sat
mon,wed,fri,sun
mon-tue,thu-sun
В качестве альтернативы, если вы предпочитаете использовать метод грубой силы, вы можете заменить запятые на дефисы для всех соседних дней, а затем использовать regex для удаления «внутренностей» нескольких дней подряд.
Код: (демо)
define(
'PAIRS',
[
[
'mon,tue',
'tue,wed',
'wed,thu',
'thu,fri',
'fri,sat',
'sat,sun'
],
[
'mon-tue',
'tue-wed',
'wed-thu',
'thu-fri',
'fri-sat',
'sat-sun'
]
]
);
function condenseDays(string $days): string
{
return preg_replace(
'/-K[^,] -/',
'',
str_replace(PAIRS[0], PAIRS[1], $days)
);
}
Самая хитрая / наименее понятная версия, в которой запятые, соответствующие диапазону, обозначаются соседней буквой вместо дней из 3 букв.
Код: (демо)
function condenseDays(string $days): string
{
return preg_replace(
'/-K[^,] -/',
'',
str_replace(
['n,t', 'e,w', 'd,t', 'u,f', 'i,s', 't,s'],
['n-t', 'e-w', 'd-t', 'u-f', 'i-s', 't-s'],
$days
)
);
}