Search code examples
phparraysmultidimensional-arraytext-parsing

Parse dot-delimited strings and make a multidimensional array


I have an array that looks like this

[1] => Array
        (
            [name] => block.0.name
            [value] => vda
        )

    [2] => Array
        (
            [name] => block.0.backingIndex
            [value] => 2
        )

    [3] => Array
        (
            [name] => block.0.rd.reqs
            [value] => 248907
        )

    [4] => Array
        (
            [name] => block.0.rd.bytes
            [value] => 9842014208
        )

    [5] => Array
        (
            [name] => block.0.rd.times
            [value] => 372870570891
        )

    [6] => Array
        (
            [name] => block.0.wr.reqs
            [value] => 6869976
        )

    [7] => Array
        (
            [name] => block.0.wr.bytes
            [value] => 50781960192
        )

    [8] => Array
        (
            [name] => block.0.wr.times
            [value] => 32361608225142
        )

    [9] => Array
        (
            [name] => block.0.fl.reqs
            [value] => 2471825
        )

    [10] => Array
        (
            [name] => block.0.fl.times
            [value] => 936802992509
        )

    [11] => Array
        (
            [name] => block.0.allocation
            [value] => 21107503104
        )

    [12] => Array
        (
            [name] => block.0.capacity
            [value] => 21474836480
        )

    [13] => Array
        (
            [name] => block.0.physical
            [value] => 21474836480
        )

    [14] => Array
        (
            [name] => block.1.name
            [value] => hda
        )

    [15] => Array
        (
            [name] => block.1.path
            [value] => /var/datastores/disk.1
        )

    [16] => Array
        (
            [name] => block.1.backingIndex
            [value] => 30
        )

    [17] => Array
        (
            [name] => block.1.rd.reqs
            [value] => 2871
        )

    [18] => Array
        (
            [name] => block.1.rd.bytes
            [value] => 9677156
        )

    [19] => Array
        (
            [name] => block.1.rd.times
            [value] => 620637479
        )

    [20] => Array
        (
            [name] => block.1.capacity
            [value] => 374784
        )

    [21] => Array
        (
            [name] => block.1.physical
            [value] => 376832
        )

I need to get the array to look something like the following

[blocks] => Array
    (
  [block0] => Array
     (
     [backingIndex] => 2
     [rd.reqs] => 2480907
     [rd.bytes] => 9842014208
     [rd.times] = > 372870570891
     ............
             ) 
   [block1] => Array
     (
      [backingIndex] => 30
      [rd.reqs] => 2871
      [rd.bytes] => 9677156
      [rd.times] = > 620637479
      ............
     )
  )

its worth noting that the array contains alot more items and will contain items like

vcpu.0.state=1
  vcpu.0.time=963654400000000
  vcpu.0.wait=0
  vcpu.1.state=1
  vcpu.1.time=936409070000000
  vcpu.1.wait=0
  vcpu.2.state=1
  vcpu.2.time=943396180000000
  vcpu.2.wait=0
  vcpu.3.state=1
  vcpu.3.time=959496330000000
  vcpu.3.wait=0

which should create a similar subset

but some values do not have the integer index such as

  balloon.current=16777216
  balloon.maximum=16777216
  balloon.swap_in=0
  balloon.swap_out=0
  balloon.major_fault=262
  balloon.minor_fault=132293
  balloon.unused=16153712
  balloon.available=16396312

I could do this by using loops and looking for specific strings but the time and overhead does not seem worth it, I would like to be able to create a sub array based on a partial string like

block.0.rd.reqs -> arrayName.index.value

and I can not seem to get it to work without over 100 lines of code and an extremely long execution time.

This information is coming from running a Virsh domstats command.


Solution

  • Typically, I'd point you toward Convert dot syntax like "this.that.other" to multi-dimensional array in PHP and hammer this question as an under-researched duplicate, but you seem to have sufficient deviation in your desired output.

    Your sample input and desired output are not well expressed in your question, but if I am reverse engineering your requirements correctly, you just need to explode the name values, do some conditional preparation, then push the values into the 3-level output array.

    Code: (Demo)

    $result = [];
    foreach ($array as ['name' => $name, 'value' => $value]) {
        $parts = explode('.', $name);
        $parentKey = $parts[0] . 's';
        $childKey = implode(array_splice($parts, 0, ctype_digit($parts[1]) ? 2 : 1));
        $grandchildKey = implode('.', $parts);  // $parts was reduced by array_splice()
        if ($grandchildKey !== 'name') {
            $result[$parentKey][$childKey][$grandchildKey] = $value;
        }
    }
    var_export($result);
    

    Output:

    array (
      'blocks' => 
      array (
        'block0' => 
        array (
          'backingIndex' => 2,
          'rd.reqs' => 248907,
          'rd.bytes' => 9842014208,
          'rd.times' => 372870570891,
        ),
        'block1' => 
        array (
          'backingIndex' => 30,
          'rd.reqs' => 2871,
          'rd.bytes' => 9677156,
          'rd.times' => 620637479,
        ),
        'block2' => 
        array (
          'backingIndex' => 30,
          'rd.reqs' => 2871,
          'rd.bytes' => 9677156,
          'rd.times' => 620637479,
        ),
      ),
      'vcpus' => 
      array (
        'vcpu0' => 
        array (
          'state' => 1,
          'time' => 963654400000000,
          'wait' => 0,
        ),
        'vcpu1' => 
        array (
          'state' => 1,
          'time' => 936409070000000,
          'wait' => 0,
        ),
        'vcpu2' => 
        array (
          'state' => 1,
          'time' => 943396180000000,
          'wait' => 0,
        ),
        'vcpu3' => 
        array (
          'state' => 1,
          'time' => 959496330000000,
          'wait' => 0,
        ),
      ),
      'balloons' => 
      array (
        'balloon' => 
        array (
          'current' => 16777216,
          'maximum' => 34534530,
          'swap_in' => 0,
          'swap_out' => 0,
          'major_fault' => 262,
          'minor_fault' => 132293,
          'unused' => 16153712,
          'available' => 16396312,
        ),
      ),
    )