Nicero
Nicero

Reputation: 4377

Convert the matches of a regex into a multidimensional array

The code below correctly matches:

and creates an array as expected as this:

Array 
(
    [0] => Array
    (
        [start] => 4
        [end]=> 8
    )        
    [1] => Array
    (
        [start] => 2
        [end]=> 
    )
    [2] => Array
    (
        [start] => 3
        [end] => 5
    )
    [3] => Array
    (
        [start] => 6[2]
        [end] =>
    )
    [4] => Array
    (
        [start] => 8[4]
        [end] => 10
    )
    [5] => Array
    (
        [start] => 14
        [end] => 21[5]
    )
)

CODE:

 $str = "a2c4-8|a6c2,c3-5,c6[2],c8[4]-10,c14-21[5]";

 $re = "~c(?<start>\d+(?:\[\d+])?)(?:-(?<end>(?&start)?))?~"; 

 $myarray = explode("|",$str);

foreach ($myarray as $my) {
    preg_match_all($re, $my, $matches);

    $res = array_map(function($ms, $me) { 
        return array("start" => $ms, "end" => $me);
    }, $matches["start"], $matches["end"]);
}

echo "<pre>";
    print_r($res);
echo "</pre>";

Now I would like to add an other key with the value of the digit after the a. So for example, given the above $str

$str = "a2c4-8|a6c2,c3-5,c6[2],c8[4]-10,c14-21[5]". 

the a values are: 2 and 6 and the expected result array would be the following. Note that 6 is repeated for every c matched.

Array 
(
    [0] => Array
    (
        [a] => 2
        [start] => 4
        [end]=> 8
    )        
    [1] => Array
    (
        [a] => 6
        [start] => 2
        [end]=> 
    )
    [2] => Array
    (
        [a] => 6
        [start] => 3
        [end] => 5
    )
    [3] => Array
    (
        [a] => 6
        [start] => 6[2]
        [end] =>
    )
    [4] => Array
    (
        [a] => 6
        [start] => 8[4]
        [end] => 10
    )
    [5] => Array
    (
        [a] => 6
        [start] => 14
        [end] => 21[5]
    )
)

Upvotes: 0

Views: 73

Answers (1)

Casimir et Hippolyte
Casimir et Hippolyte

Reputation: 89557

You can do that:

$str = "a2c4-8|a6c2,c3-5,c6[2],c8[4]-10,c14-21[5]";

$re = '~(?:a(?<a>\d+))?c(?<start>\d+(?:\[\d+])?)(?:-(?<end>\g<start>))?~';

if (preg_match_all($re, $str, $matches, PREG_SET_ORDER) ) {
    $a = ''; // current value for "a" -----,
    $result = array_map(function ($i) use (&$a) {
        if (!empty($i['a'])) $a = $i['a'];
        else $i['a'] = $a;

        if (!isset($i['end'])) $i['end'] = '';

        foreach ($i as $k=>$v) {
            if (is_numeric($k)) unset($i[$k]);
        }

        return $i;

    }, $matches);
}

print_r($result);

But if you already know exactly how is the string format and if you don't need to validate it, there are probably smarter (and faster) ways to obtain the same result.

For example, using explode and formatted strings with sscanf:

$str = "a2c4-8|a6c2,c3-5,c6[2],c8[4]-10,c14-21[5]";

$parts = explode('|', $str);
$result = [];

$keys = ['a', 'start', 'end'];

foreach($parts as $part) {
    foreach(explode(',', $part) as $k=>$v) {
        if ($k)
            list($start, $end) = sscanf($v, 'c%[^-]-%[^,]');
        else
            list($a, $start, $end) = sscanf($v, 'a%dc%[^-]-%[^,]');

        $result[] = array_combine($keys, [$a, $start, $end]);
    }
}

print_r($result);

Upvotes: 1

Related Questions