开发者

Populate array of integers from a comma-separated string of numbers and hyphenated number ranges

开发者 https://www.devze.com 2023-04-11 21:03 出处:网络
I want to translate/hydrate/expand/parse a comma-separated string of integers and hyhenated integer range expressions and 开发者_开发知识库populate an array with its equivalent values as individual in

I want to translate/hydrate/expand/parse a comma-separated string of integers and hyhenated integer range expressions and 开发者_开发知识库populate an array with its equivalent values as individual integers elements.

Input strings might look like the following:

3,5,6,9,11,23

or

3-20

or

3-6,8,12,14-20

I want to return these as an array of integers, so the last one would become:

[3,4,5,6,8,12,14,15,16,17,18,19,20]

Is there either a function available that does this, or how would I start in writing one?


You are probably looking for the range function and implode:

$input = '3,5,6,9,11,23,14-77';

$result = preg_replace_callback('/(\d+)-(\d+)/', function($m) {
    return implode(',', range($m[1], $m[2]));
}, $input);

gives you:

3,5,6,9,11,23,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77

Demo

How it works: You basically have two tokens in your string: the range-token (1-n) (defined as regular expression: (\d+)-(\d+)) and the fallback (anything else).

preg_replace_callback allows the expansion of the token string by a callback function. That callback function then just expands the two matched numerical values into the comma-separated list by using PHP's range function and implode.

After that the string is in a normalized format you can just explode it:

// as array:

print_r(explode(',', $result));

Full Demo


And after years as it was requested well formulated, the integer array, you can easily treat it as a JSON Array:

$result = json_decode('['. preg_replace_callback('/(\d+)-(\d+)/', function($m) {
    return implode(',', range($m[1], $m[2]));
}, $input) .']');
var_dump($result);

Demo PHP 5.3-8.1 + Git Master


Here's a formalization/formulation of hakre's excellent answer:

function rangesToList($a, $max) {
    $a = trim($a);
    if (isset($max) && substr($a, -1) == "-") {
        $a .= $max;
    }
    $r = preg_replace_callback('/(\d+)-(\d+)/', function($m) {
        return implode(',', range($m[1], $m[2]));
    }, $a);
    return array_map('intval', explode(",", $r));
}

In addition to being encapsulated into a formula and a little more robust, it also takes an optional $max argument so that you can give it input like this:

$in = "2,4,6-8, 12-";
$out = rangesToList($in, 14);

and get this

Array
(
    [0] => 2
    [1] => 4
    [2] => 6
    [3] => 7
    [4] => 8
    [5] => 12
    [6] => 13
    [7] => 14
)


function list2array ($list) {
    $array = explode(',', $list);
    $return = array();
    foreach ($array as $value) {
        $explode2 = explode('-', $value);
        if (count($explode2) > 1) {
            $range = range($explode2[0], $explode2[1]); 
            $return = array_merge($return, $range);
        } else {
            $return[] = (int) $value;
        }
    }
    return $return;
}

Result:

php > print_r(list2array('3-6,8,12,14-50'));
Array
(
    [0] => 3
    [1] => 4
    [2] => 5
    [3] => 6
    [4] => 8
    [5] => 12
    [6] => 14
    [7] => 15
    [...]
    [41] => 49
    [42] => 50
)

It doesn't have error reporting by the way, you can put that in there by yourself :).


I suppose the potential attraction to my snippet will be its brevity. After exploding the input string on commas, it unconditionally generates a range of one or more integers and pushes them into the result array. When there is no hyphenated range expression, the lone number is passed to range() as the first and second parameter.

Code: (Demo)

$result = [];
foreach (explode(',', $string) as $v) {
    $x = explode('-', $v);
    array_push($result, ...range($x[0], $x[1] ?? $v));
}
var_export($result);

It may be more performant to use a condition to avoid excess function calls. sscanf() will parse the string, convert numbers to integers, and return the number of assignments that it made.

Code: (Demo)

$result = [];
foreach (explode(',', $string) as $v) {
    if (sscanf($v, '%d-%d', $start, $end) === 1) {
        $result[] = $start;
    } else {
        array_push($result, ...range($start, $end));
    }
}
var_export($result);

To tweak @hakre's snippet to return int type values, you can wrap the modified string in square braces and call json_decode(). Because the numeric values are not quote-wrapped, json_decode will return int typed values.

Code: (Demo)

var_export(
    json_decode(
        '['
        . preg_replace_callback(
              '/(\d+)-(\d+)/',
              fn($m) => implode(',', range($m[1], $m[2])),
              $string
          )
        . ']'
    )
);


I don't any function. You could explode the string by "," and then check if it is a range expression and insert the values.

Example code (don't know how the array should look like & doesn't support negative values but could be easily added):

$input = "3-6,8,12";
$output = array();


$strings = explode(",", $input);
foreach( $strings as $value ) {
    if( stripos( $value, "-") === false ) {
        $output[(int) $value] = (int) $value;
    } else {
        $range = explode("-", $value);

        $range[0] = (int) $range[0];
        $range[1] = (int) $range[1];

        if( $range[0] <= $range[0] ) {
            for ($i = $range[0]; $i <= $range[1]; $i++) {
                $output[$i] = $i;
            }
        }
    }
}

var_dump( $output );

This produces output:

array(6) { [3]=> int(3) [4]=> int(4) [5]=> int(5) [6]=> int(6) [8]=> int(8) [12]=> int(12) } 


$str = '2,3,4,5-10';
$strArr = explode(',',$str);
$finalArr = array();
$m = array();
foreach($strArr as $v) {
    $m = preg_split(":-:",$v);
    $start = $m[0];
    $finish = (isset($m[1]) ? $m[1] : $m[0]);
    $range = range($start,$finish);
    foreach($range as $r) $finalArr[] = $r;
}
print_r($finalArr);


<?php
    $input="1,2,5-9,12,19";
    $iarr=explode(",", $input);



    $oarr=array();

    $rex='/([0-9]{1,})\-([0-9]{1,})/';

    for($i=0; $i<count($iarr); $i++) {
        preg_match($rex, $iarr[$i], $arrs);
        if(!$arrs) {
            array_push($oarr, $iarr[$i]);
        } else {
            $oarr=array_merge($oarr, range($arrs[1], $arrs[2], 1));
            /* Save a few lines of code by removing this and using above. Same difference, though
            for($j=$arrs[1]; $j<=$arrs[2]; $j++) {
                array_push($oarr, $j);
            } */
        }
    }



    print_r($oarr);
?>

Result:

Array (
    [0] => 1
    [1] => 2
    [2] => 5
    [3] => 6
    [4] => 7
    [5] => 8
    [6] => 9
    [7] => 12
    [8] => 19 
) 


One more function:

function arrayFromList($list){
    $return = array();
    foreach(explode(',',$list) as $item){
        if(($dash = strpos($item, '-', 1)) !== FALSE){
            $return = array_merge($return, range(substr($item, 0, $dash),substr($item,$dash+1)));
        }
        else{
            $return[] = (int)$item;
        }
    }
    return $return;
}

This one is aware of negative numbers if you need it, so input like -4--1,1 will also work.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号