отсутствующий список номеров php с разделенными - когда серия чисел отсутствует [dубликат]

Я работаю над массивом числовых значений.

У меня есть массив числовых значений, как показано ниже в PHP

11,12,15,16,17,18,22,23,24

И я пытаюсь преобразовать он в диапазон, например, в приведенном выше случае это будет:

11-12,15-18,22-24

Я не знаю, как преобразовать его в диапазон.

3
задан 8 June 2014 в 13:04

5 ответов

Просто добавив мою копию, которая немного отличается и поддерживает несколько дополнительных вещей. Я пришел сюда, чтобы сравнить его с другими реализациями. Вот тестовый код для проверки возможности / правильности моего кода:

$tests = [
    '1, 3, 5, 7, 9, 11, 13-15' => [1, 3, 5, 7, 9, 11, 13, 14, 15],
    '1-5'                      => [1, 2, 3, 4, 5],
    '7-10'                     => [7, 8, 9, 10],
    '1-3'                      => [1, 2, 3],
    '1-5, 10-12'               => [1, 2, 3, 4, 5, 10, 11, 12],
    '1-5, 7'                   => [1, 2, 3, 4, 5, 7],
    '10, 12-15'                => [10, 12, 13, 14, 15],
    '10, 12-15, 101'           => [10, 12, 13, 14, 15, 101],
    '1-5, 7, 10-12'            => [1, 2, 3, 4, 5, 7, 10, 11, 12],
    '1-5, 7, 10-12, 101'       => [1, 2, 3, 4, 5, 7, 10, 11, 12, 101],
    '1-5, 7, 10, 12, 14'       => [1, 2, 3, 4, 5, 7, 10, 12, 14],
    '1-4, 7, 10-12, 101'       => '1,2,3,4,7,10,11,12,101',
    '1-3, 5.5, 7, 10-12, 101'  => '1,2,3,5.5,7,10,11,12,101',
];

foreach($tests as $expectedResult => $array) {
    $funcResult = Utility::rangeToStr($array);
    if($funcResult != $expectedResult) {
        echo "Failed: result '$funcResult' != test check '$expectedResult'\n";
    } else {
        echo "Passed!: '$funcResult' == '$expectedResult'\n";
    }
}

Мясо и картофель, это означает, что он называется статически внутри класса, просто удалите «статическую публикацию» для использования в качестве нормальная процессуальная функция:

/**
 * Converts either a array of integers or string of comma-separated integers to a natural english range, such as "1,2,3,5" to "1-3, 5".  It also supports
 * floating point numbers, however with some perhaps unexpected / undefined behaviour if used within a range.
 *
 * @param string|array $items    Either an array (in any order, see $sort) or a comma-separated list of individual numbers.
 * @param string       $itemSep  The string that separates sequential range groups.  Defaults to ', '.
 * @param string       $rangeSep The string that separates ranges.  Defaults to '-'.  A plausible example otherwise would be ' to '.
 * @param bool|true    $sort     Sort the array prior to iterating?  You'll likely always want to sort, but if not, you can set this to false.
 *
 * @return string
 */
static public function rangeToStr($items, $itemSep = ', ', $rangeSep = '-', $sort = true) {
    if(!is_array($items)) {
        $items = explode(',', $items);
    }
    if($sort) {
        sort($items);
    }
    $point = null;
    $range = false;
    $str = '';
    foreach($items as $i) {
        if($point === null) {
            $str .= $i;
        } elseif(($point + 1) == $i) {
            $range = true;
        } else {
            if($range) {
                $str .= $rangeSep . $point;
                $range = false;
            }
            $str .= $itemSep . $i;
        }
        $point = $i;
    }
    if($range) {
        $str .= $rangeSep . $point;
    }

    return $str;
}
1
ответ дан 15 August 2018 в 14:38

http://ideone.com/lmd7SY

Это мои ребята. Алгоритм совершенно ясен из того, как я его закодировал, с комментариями через регулярные промежутки времени. Подход состоял в том, чтобы разделить проблему на подчасти, а вот псевдокод.

Пройти через отсортированный массив, используя цикл для обнаружения точек останова. Если следующий и предыдущий числа находятся в последовательном продвижении n + 1, ничего не делайте, иначе обратите внимание, что точка останова теперь обнаружена. Отслеживайте эту точку разрыва, нажав клавишу разрыва в новый массив. Используя данные из массива точек останова, используйте ключевую информацию, в которой точки прорыва должны проходить через начальный массив, чтобы строить значения диапазона. Убедитесь, что вы проверили последнюю итерацию.
$array = array(11,12,15,16,17,18,22,23,24);
$break_start = array();

//range finder
for ($i=0; $i<sizeof($array); $i++) {
    $current = $array[$i]; 
    $previous = $array[$i-1];
    if ($current==($previous+1)) { 
        //no break points are found 
    } else { 
        //return break points with keys intact
        array_push($break_start, $i);
    }

}

for ($i=0; $i<sizeof($break_start); $i++) {
    $key = $break_start[$i];
    $next_key = $break_start[$i+1];

    //if last iteration
    if ($i==sizeof($break_start)-1) { 
        echo "Range: ".$array[$key]." - ".$array[count($array)-1]." \n"; 
        } 
    else { 
        echo "Range: ".$array[$key]." - ".$array[$next_key-1]." \n";    
        }
}

Довольно сплошное и ручное кодирование примерно за 10 минут.

Также работает и для больших наборов:

$array = array(11,12,15,16,17,18,22,23,24,26,27,28,56,57,58);

0
ответ дан 15 August 2018 в 14:38

Я использовал это раньше, он делает трюк.

Принимает в качестве входной строки чисел, разделенных запятыми. Вызов sort может быть проигнорирован, если числа гарантированно будут отсортированы уже.

function range_string($csv)
{
    // split string using the , character
    $number_array = array_map('intval', explode(',', $csv));
    sort($number_array);

    // Loop through array and build range string
    $previous_number = intval(array_shift($number_array)); 
    $range = false;
    $range_string = "" . $previous_number; 
    foreach ($number_array as $number) {
      $number = intval($number);
      if ($number == $previous_number + 1) {
        $range = true;
      }
      else {
        if ($range) {
          $range_string .= "-$previous_number";
          $range = false;
        }
        $range_string .= ",$number";
      }
      $previous_number = $number;
    }
    if ($range) {
      $range_string .= "-$previous_number";
    }

    return $range_string;
}

$csv_string = "11,16,12,17,18,15,22,23,24";
print range_string($csv_string); // 11-12,15-18,22-24
2
ответ дан 15 August 2018 в 14:38
  • 1
    Большое вам спасибо за ваш прямой код, но я работал над ним и amp; разработанный код ... так что sry, но на самом деле спасибо за предоставление мне прямого кода .... – Vishal Shetty 8 June 2014 в 13:14

Если у нас есть предыдущий элемент и текущий элемент не является следующим числом в последовательности, тогда мы помещаем предыдущий диапазон (start-prev) в выходной массив, и текущий элемент будет началом следующего диапазона, если у нас нет предыдущего элемента, то этот элемент является первым и, как упоминалось ранее, первым элементом начинает новый диапазон. Функция newItem возвращает диапазон или номер символа, если нет диапазона. Если у вас есть несортированный массив с повторяющимися числами, используйте функции sort () и array_unique ().

$arr = array(1,2,3,4,5,7,9,10,11,12,15,16);

function newItem($start, $prev)
{
    if ($start == $prev)
    {
        $result = $start;
    }
    else
    {
        $result = $start . '-' . $prev;
    }

    return $result;
}

foreach($arr as $item)
{
    if ($prev)
    {
        if ($item != $prev + 1)
        {
            $newarr[] = newItem($start, $prev);
            $start = $item;
        }
    }
    else
    {
        $start = $item;
    }
    $prev = $item;
}

$newarr[] = newItem($start, $prev);

echo implode(',', $newarr);

1-5,7,9-12,15-16

1
ответ дан 15 August 2018 в 14:38
  • 1
    Помимо показа кода, пожалуйста, также объясните, как он работает, чтобы дать полный и понятный ответ. – SOFe 20 September 2016 в 20:53
  • 2
    Спасибо за ваш комментарий, я надеюсь, что теперь мой пост выглядит более понятным. – serg2k 20 September 2016 в 22:45

Ответ, предоставленный Али Гаджани, продолжал давать мне предупреждения «Незаконное смещение». Поэтому, поскольку я полагал, что кто-то может захотеть использовать его так же плохо, как и я, я размещаю свои исправления здесь, но помните, что мои исправления могут быть глупыми для продвинутого программиста - похоже, он работает без проблем. [ ! d0]

Я заменил / отредактировал две части кода. Ниже вы увидите, что я добавил (выделено полужирным шрифтом) и что было удалено, что я прокомментировал (//).

Как я могу судить, у него были проблемы с первым пропуском, потому что нет «предыдущего прохода» для ссылки (следовательно, он искажал «$ previous = $ array [$ i-1];») - и на последнем проходе по той же причине. В этом втором экземпляре я просто переместил «$ next_key = $ break_start [$ i + 1];» ниже последней проверки итерации.

$break_start = array();

//range finder
for ($i=0; $i<sizeof($array); $i++) {
    $current = $array[$i]; 
**if($i>0) {
        $previous = $array[$i-1];
}
else {
        $previous = $current;
}**
  //  $previous = $array[$i-1];
    if ($current==($previous+1)) { 
        //no break points are found 
    } else { 
        //return break points with keys intact
        array_push($break_start, $i);
    }

}

for ($i=0; $i<sizeof($break_start); $i++) {
    $key = $break_start[$i];
//    $next_key = $break_start[$i+1];

    //if last iteration
    if ($i==sizeof($break_start)-1) { 
        echo "Range: ".$array[$key]." - ".$array[count($array)-1]." \n"; 
        } 
    else { 
    **$next_key = $break_start[$i+1];**
        echo "Range: ".$array[$key]." - ".$array[$next_key-1]." \n";    
        }
}

Если есть более разумные способы сделать это, проконсультируйтесь. Но я искал высоко и низко для этого - и подумал, что кому-то еще может быть полезно. Еще один верхушка шляпы Али Гаджани за его первоначальную помощь.

0
ответ дан 15 August 2018 в 14:38
  • 1
    Ну, я говорил слишком рано ... Если я подключу $ array = array (1,2,4,5,7); в приведенный выше код, я получаю «Диапазон: 1 - 2 Диапазон: 4 - 5 Диапазон: 7 - 7». - последняя цифра повторяется. – tvsmvp 19 March 2016 в 05:11

Другие вопросы по тегам:

Похожие вопросы: