在php中将数组元素转换为范围

在php中将数组元素转换为范围,php,arrays,range,Php,Arrays,Range,我正在处理一个数值数组 在PHP中,我有一个如下所示的数值数组 11,12,15,16,17,18,22,23,24 我试图将其转换为范围,例如,在上述情况下: 11-12,15-18,22-24 我不知道如何将其转换为范围。您必须自己编写;-) 算法非常简单: 对项目进行迭代 记住上一项和范围的开始 对于每个项目(第一个项目除外),请检查: 如果currentItem=previItem+1,则未找到新范围。继续 否则你的射程就结束了。写下范围。您已经记住了范围的开始。结尾是上一项。新

我正在处理一个数值数组

在PHP中,我有一个如下所示的数值数组

11,12,15,16,17,18,22,23,24
我试图将其转换为范围,例如,在上述情况下:

11-12,15-18,22-24

我不知道如何将其转换为范围。

您必须自己编写;-)

算法非常简单:

  • 对项目进行迭代
  • 记住上一项和范围的开始
  • 对于每个项目(第一个项目除外),请检查:
    • 如果
      currentItem=previItem+1
      ,则未找到新范围。继续
    • 否则你的射程就结束了。写下范围。您已经记住了范围的开始。结尾是上一项。新范围从当前项开始
    • 第一项总是开始一个新范围。记住这一个作为范围的开始
  • 离开循环时,不要忘记记下当前范围

我以前用过这个,它很管用

将逗号分隔的数字字符串作为输入。如果保证已对数字进行排序,则可以忽略对
sort
的调用

这是我的版本。从我编写代码的方式来看,这个算法非常明显,每隔一段时间就有注释。方法是将问题分成子部分,下面是伪代码

使用循环遍历已排序的数组以检测断点。如果下一个和上一个数字的后续级数为n+1,则不执行任何操作,否则,请注意现在已检测到断点。将break键按入新数组,跟踪该断点。使用断点数组中的数据,使用断点所在的关键信息在初始数组中循环以构建范围值。确保检查最后一次迭代


只需添加我的副本,该副本略有不同,并支持一些额外的内容。我来这里是为了将它与其他实现进行比较。下面是测试代码,用于检查我的代码的功能/正确性:

$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";
    }
}
肉和土豆,这意味着在类中静态调用,但只需删除“static public”即可作为正常的过程函数使用:

/**
 * 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;
}

阿里·加贾尼提供的答案一直在警告我“非法抵消”。所以,因为我认为有人可能会像我一样想使用它,所以我在这里发布了我的修复程序——尽管请注意,我的修复程序可能会被一个高级程序员认为是愚蠢的——它现在似乎可以正常工作了

我替换/调整了代码的两部分。下面您将看到我添加的内容(用粗体标记)和删除的内容,我对此进行了注释(//)

据我所知,它在第一个过程中遇到了问题,因为没有“上一个过程”可供参考(因此,它在“$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";    
        }
}
$break_start=array();
//测距仪
对于($i=0;$i0){
$previous=$array[$i-1];
}
否则{
$previous=$current;
}**
//$previous=$array[$i-1];
如果($current==($previous+1)){
//未找到断点
}否则{
//返回键完整的断点
数组推送($break\u start,$i);
}
}

对于($i=0;$i如果我们有上一个项目,当前项目不是序列中的下一个数字,那么我们将上一个范围(开始上一个)放入输出数组,当前项目将是下一个范围的开始,如果我们没有上一个项目,那么这个项目是第一个项目,如前所述-第一个项目开始一个新的范围。 如果没有范围,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你可以这样做:

$numbers = [11,12,15,16,17,18,22,23,24];
$ranges  = [];
$start   = $end = current($numbers);

foreach($numbers as $range){
    if($range - $end > 1){
        $ranges[] = ($start == $end) ? $start : $start . "-" . $end;
        $start    = $range;
    }
    $end = $range;
}
$ranges[] = ($start == $end) ? $start : $start . "-" . $end;

相关:(但在JavaScript中)好吧,我说得太快了…如果我在上面提供的代码中插入$array=array(1,2,4,5,7);我会得到“Range:1-2 Range:4-5 Range:7-7”-最后一个数字会重复。
$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";    
        }
}
$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);
$numbers = [11,12,15,16,17,18,22,23,24];
$ranges  = [];
$start   = $end = current($numbers);

foreach($numbers as $range){
    if($range - $end > 1){
        $ranges[] = ($start == $end) ? $start : $start . "-" . $end;
        $start    = $range;
    }
    $end = $range;
}
$ranges[] = ($start == $end) ? $start : $start . "-" . $end;