0001
0001

Reputation: 9

Convert text with 1 delimiting string into an array with associative rows

I am having this string:

0&39avfy;&39avfy;task&39avfy;rose&39avfy;duration&39avfy;1.25&39avfy;user&39avfy;15&39avfy;1&39avfy;&39avfy;task&39avfy;daisy&39avfy;duration&39avfy;0.75&39avfy;user&39avfy;25&39avfy;2&39avfy;&39avfy;task&39avfy;orchid&39avfy;duration&39avfy;1.15&39avfy;user&39avfy;7

I need a 2d array like this:

array( 
    array(
        "task" => "rose", 
        "duration" => 1.25,
        "user" => 15 
    ),
    array(
        "task" => "daisy", 
        "duration" => 0.75,
        "user" => 25,
    ),
    array(
        "task" => "orchid", 
        "duration" => 1.15,
        "user" => 7 
    )
);

Upvotes: 0

Views: 599

Answers (3)

mickmackusa
mickmackusa

Reputation: 47894

Here is a way to split the string into an array of lines/rows, then parse each row with sscanf().

Code: (Demo)

$input = "0&39avfy;&39avfy;task&39avfy;rose&39avfy;duration&39avfy;1.25&39avfy;user&39avfy;15&39avfy;1&39avfy;&39avfy;task&39avfy;daisy&39avfy;duration&39avfy;0.75&39avfy;user&39avfy;25&39avfy;2&39avfy;&39avfy;task&39avfy;orchid&39avfy;duration&39avfy;1.15&39avfy;user&39avfy;7";

$delim = '&39avfy;';

$result = [];
foreach (preg_split('/' . $delim . '(?=\d+' . $delim . $delim . ')/', $input) as $row) {
    [
        $i,
        $k,
        $result[$i][$k],
        $k,
        $result[$i][$k],
        $k,
        $result[$i][$k]
    ] = sscanf($row, "%[^&]$delim" . str_repeat("$delim%[^&]", 6));
}
var_export($result);

Or if you don't know how many key-value pairs to expect, use the \G (continue metacharacter) to continue matching key-value pairs after encountering the row id number. Demo

$regex = <<<REGEX
/
(\d+)&39avfy;
|\G(?!^)&39avfy;([^&]+)&39avfy;([^&]+)
/x
REGEX;
preg_match_all($regex, $input, $m, PREG_SET_ORDER);

$result = [];
foreach ($m as $set) {
    if (strlen($set[1])) {
        $index = $set[1];
    } else {
        $result[$index][$set[2]] = $set[3];
    }
    
}
var_export($result);

Output (from either snippet):

array (
  0 => 
  array (
    'task' => 'rose',
    'duration' => '1.25',
    'user' => '15',
  ),
  1 => 
  array (
    'task' => 'daisy',
    'duration' => '0.75',
    'user' => '25',
  ),
  2 => 
  array (
    'task' => 'orchid',
    'duration' => '1.15',
    'user' => '7',
  ),
)

Upvotes: 0

Jakob Alexander Eichler
Jakob Alexander Eichler

Reputation: 3056

At least this solves your problem.

<?php

$in = '0&39avfy;&39avfy;task&39avfy;rose&39avfy;duration&39avfy;1.25&39avfy;user&39avfy;15&39avfy;1&39avfy;&39avfy;task&39avfy;daisy&39avfy;duration&39avfy;0.75&39avfy;user&39avfy;25&39avfy;2&39avfy;&39avfy;task&39avfy;orchid&39avfy;duration&39avfy;1.15&39avfy;user&39avfy;7';

$arr = explode('&39avfy;',$in);
$out = array();
$i = 0;
foreach($arr as $a){
    switch ($i) {
        case 0:
            if(isset($tmp)) { $out[] = $tmp; }
            break;
        case 3:
            $tmp["task"] = $a;
            break;
        case 5:
            $tmp["duration"] = $a;
            break;
        case 7:
            $tmp["user"] = $a;
            break;
        default:
    }
    $i++;
    $i%=8;
}
if($i == 0 && isset($tmp)) { $out[] = $tmp; }
var_dump($out);

array(3) {
  [0]=>
  array(3) {
    ["task"]=>
    string(4) "rose"
    ["duration"]=>
    string(4) "1.25"
    ["user"]=>
    string(2) "15"
  }
  [1]=>
  array(3) {
    ["task"]=>
    string(5) "daisy"
    ["duration"]=>
    string(4) "0.75"
    ["user"]=>
    string(2) "25"
  }
  [2]=>
  array(3) {
    ["task"]=>
    string(6) "orchid"
    ["duration"]=>
    string(4) "1.15"
    ["user"]=>
    string(1) "7"
  }
}

this code leads to the same result:

<?php

$in = '0&39avfy;&39avfy;task&39avfy;rose&39avfy;duration&39avfy;1.25&39avfy;user&39avfy;15&39avfy;1&39avfy;&39avfy;task&39avfy;daisy&39avfy;duration&39avfy;0.75&39avfy;user&39avfy;25&39avfy;2&39avfy;&39avfy;task&39avfy;orchid&39avfy;duration&39avfy;1.15&39avfy;user&39avfy;7';

$matches = array();
$regex = '/(\\d)&39avfy;&39avfy;task&39avfy;(.+?)&39avfy;duration&39avfy;(\\d+\\.\\d+)&39avfy;user&39avfy;(\\d+)/';
preg_match_all($regex, $in, $matches, PREG_SET_ORDER  );
$out = array();
foreach($matches as $m){
    $tmp = array();
    $tmp['task'] = $m[2];
    $tmp['duration'] = $m[3];
    $tmp['user'] = $m[4];
    $out[$m[1]] = $tmp;
}
var_dump($out);

Upvotes: 1

mario
mario

Reputation: 145482

Well, since it is uniformly ordered you can use this approach:

 preg_match_all('#
                  task      &39avfy;  (?<task> [^&]*)  .*?
                  duration  &39avfy;  (?<duration> [^&]*) .*?
                  user      &39avfy;  (?<user> [^&]*)
              \K #x',
     $str, $matches, PREG_SET_ORDER);

It will have some numeric array entries, but you can filter them out. More importantly, this is easier to adapt should your delimiter change.

print_r($matches);

[0] => Array
    (
        [0] => 
        [task] => rose
        [1] => rose
        [duration] => 1.25
        [2] => 1.25
        [user] => 15
        [3] => 15
    )

[1] => Array
    (
        [0] => 
        [task] => daisy
        [1] => daisy
        [duration] => 0.75
        [2] => 0.75
        [user] => 25
        [3] => 25
    )

Upvotes: 2

Related Questions