Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Converting a range or partial array, in the form "3-6" or "3-6,12", into an array of integers, like 3,4,5,6,12

Tags:

arrays

php

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?

like image 632
Matthew Higgins Avatar asked Oct 08 '11 17:10

Matthew Higgins


2 Answers

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

like image 143
hakre Avatar answered Nov 03 '22 09:11

hakre


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
)
like image 21
JohnK Avatar answered Nov 03 '22 11:11

JohnK