errata
errata

Reputation: 6031

Hierarchically group data from a 2d array by multiple columns and use most recently encountered identifying column values when empty

I have an array in PHP:

array (size=xxx)
  0 => 
    array (size=9)
      'cat' => string 'FIRST CAT'
      'dur' => string '10'
      'type' => string 'Type description 10'
      'start' => string '00:00'
      'end' => string '01:00'
      'desc' => string 'Event description 10-1'
      'price' => string 'xxx'
      'extra' => string 'yyy'
  1 => 
    array (size=9)
      'cat' => string ''
      'dur' => string ''
      'type' => string ''
      'start' => string '01:00'
      'end' => string '02:00'
      'desc' => string 'Event description 10-2'
      'price' => string ''
      'extra' => string ''
  2 => 
    array (size=9)
      'cat' => string ''
      'dur' => string ''
      'type' => string ''
      'start' => string '02:00'
      'end' => string '03:00'
      'desc' => string 'Event description 10-3'
      'price' => string ''
      'extra' => string ''
  3 => 
    array (size=9)
      'cat' => string ''
      'dur' => string '20'
      'type' => string 'Type description 20'
      'start' => string '00:00'
      'end' => string '01:00'
      'desc' => string 'Event description 20-1'
      'price' => string 'xxx'
      'extra' => string 'yyy'
  4 => 
    array (size=9)
      'cat' => string ''
      'dur' => string ''
      'type' => string ''
      'start' => string '01:00'
      'end' => string '02:00'
      'desc' => string 'Event description 20-2'
      'price' => string ''
      'extra' => string ''
  5 => 
    array (size=9)
      'cat' => string ''
      'dur' => string ''
      'type' => string 'Type description 21'
      'start' => string '00:00'
      'end' => string '01:00'
      'desc' => string 'Event description 21-1'
      'price' => string 'xxx'
      'extra' => string 'yyy'
  6 => 
    array (size=9)
      'cat' => string ''
      'dur' => string ''
      'type' => string ''
      'start' => string '01:00'
      'end' => string '02:00'
      'desc' => string 'Event description 21-2'
      'price' => string ''
      'extra' => string ''
  7 => 
    array (size=9)
      'cat' => string 'SECOND CAT'
      'dur' => string '10'
      'type' => string 'Type description 100'
      'start' => string '00:00'
      'end' => string '01:00'
      'desc' => string 'Event description 100-1'
      'price' => string 'xxx'
      'extra' => string 'yyy'
  8 => 
    array (size=9)
      'cat' => string ''
      'dur' => string ''
      'type' => string ''
      'start' => string '01:00'
      'end' => string '02:00'
      'desc' => string 'Event description 100-2'
      'price' => string ''
      'extra' => string ''
  9 => 
    array (size=9)
      'cat' => string ''
      'dur' => string '20'
      'type' => string 'Type description 200'
      'start' => string '00:00'
      'end' => string '01:00'
      'desc' => string 'Event description 200-1'
      'price' => string 'xxx'
      'extra' => string 'yyy'
  10 => 
    array (size=9)
      'cat' => string ''
      'dur' => string ''
      'type' => string ''
      'start' => string '01:00'
      'end' => string '02:00'
      'desc' => string 'Event description 200-2'
      'price' => string ''
      'extra' => string ''
  11 => 
    array (size=9)
      'cat' => string ''
      'dur' => string ''
      'type' => string 'Type description 210'
      'start' => string '00:00'
      'end' => string '01:00'
      'desc' => string 'Event description 210-1'
      'price' => string 'xxx'
      'extra' => string 'yyy'
  etc...

I need to group this array first by cat, then by dur, then type. This type array would have two strings - price and extra and another array which would consist of all the events data - start, end and desc. Final array outline (based on example above) should look like this:

'FIRST CAT'                                           // 'cat'
    '10'                                              // 'dur'
      'Type description 10', 'xxx', 'yyy'             // 'type', 'price', 'extra'
          (array of events)
          '00:00', '01:00', 'Event description 10-1'  // 'start', 'end', 'desc'
          '01:00', '02:00', 'Event description 10-2'  // 'start', 'end', 'desc'
          '02:00', '03:00', 'Event description 10-3'  // 'start', 'end', 'desc'
    '20'
      'Type description 20', 'xxx', 'yyy'
          (array of events)
          '00:00', '01:00', 'Event description 20-1'
          '01:00', '02:00', 'Event description 20-2'
      'Type description 21', 'xxx', 'yyy'
          (array of events)
          '00:00', '01:00', 'Event description 21-1'
          '01:00', '02:00', 'Event description 21-2'
'SECOND CAT'
    '10'
      'Type description 100', 'xxx', 'yyy'
          (array of events)
          '00:00', '01:00', 'Event description 100-1'
          '01:00', '02:00', 'Event description 100-2'
    '20'
      'Type description 200', 'xxx', 'yyy'
          (array of events)
          '00:00', '01:00', 'Event description 200-1'
          '01:00', '02:00', 'Event description 200-2'
      'Type description 210', 'xxx', 'yyy'
          (array of events)
          '00:00', '01:00', 'Event description 210-1'
          '01:00', '02:00', 'Event description 210-2'

What would be the simplest way to convert my existing array to the one I need in the end?

Upvotes: -1

Views: 304

Answers (2)

mickmackusa
mickmackusa

Reputation: 47864

To preserve accessibility to relatable data while traversing the input array, I'm using reference variables because cat and dur values may be empty in some rows. Demo

$result = [];
foreach ($array as $row) {
    if ($row['cat']) {
        // kill prior cat reference when new cat is non-empty
        unset($catRef);
        // start a new cat reference
        $result[$row['cat']] =& $catRef;
    }
    if ($row['dur']) {
        // kill prior dur reference when new dur is non-empty
        unset($durEventsRef);
        // push event-shared data into dur subarray
        $catRef[$row['dur']] = [
            'type' => $row['type'],
            'price' => $row['price'],
            'extra' => $row['extra'],
            'events' => [],
        ];
        // start a new dur-events reference
        $catRef[$row['dur']]['events'] =& $durEventsRef;
    }
    // push data into dur-event reference
    $durEventsRef[] = [
        'start' => $row['start'],
        'end' => $row['end'],
        'desc' => $row['desc'],
    ];
}
var_export($result);

Output:

array (
  'FIRST CAT' => 
  array (
    10 => 
    array (
      'type' => 'Type description 10',
      'price' => 'xxx',
      'extra' => 'yyy',
      'events' => 
      array (
        0 => 
        array (
          'start' => '00:00',
          'end' => '01:00',
          'desc' => 'Event description 10-1',
        ),
        1 => 
        array (
          'start' => '01:00',
          'end' => '02:00',
          'desc' => 'Event description 10-2',
        ),
        2 => 
        array (
          'start' => '02:00',
          'end' => '03:00',
          'desc' => 'Event description 10-3',
        ),
      ),
    ),
    20 => 
    array (
      'type' => 'Type description 20',
      'price' => 'xxx',
      'extra' => 'yyy',
      'events' => 
      array (
        0 => 
        array (
          'start' => '00:00',
          'end' => '01:00',
          'desc' => 'Event description 20-1',
        ),
        1 => 
        array (
          'start' => '01:00',
          'end' => '02:00',
          'desc' => 'Event description 20-2',
        ),
        2 => 
        array (
          'start' => '00:00',
          'end' => '01:00',
          'desc' => 'Event description 21-1',
        ),
        3 => 
        array (
          'start' => '01:00',
          'end' => '02:00',
          'desc' => 'Event description 21-2',
        ),
      ),
    ),
  ),
  'SECOND CAT' => 
  array (
    10 => 
    array (
      'type' => 'Type description 100',
      'price' => 'xxx',
      'extra' => 'yyy',
      'events' => 
      array (
        0 => 
        array (
          'start' => '00:00',
          'end' => '01:00',
          'desc' => 'Event description 100-1',
        ),
        1 => 
        array (
          'start' => '01:00',
          'end' => '02:00',
          'desc' => 'Event description 100-2',
        ),
      ),
    ),
    20 => 
    array (
      'type' => 'Type description 200',
      'price' => 'xxx',
      'extra' => 'yyy',
      'events' => 
      array (
        0 => 
        array (
          'start' => '00:00',
          'end' => '01:00',
          'desc' => 'Event description 200-1',
        ),
        1 => 
        array (
          'start' => '01:00',
          'end' => '02:00',
          'desc' => 'Event description 200-2',
        ),
        2 => 
        array (
          'start' => '00:00',
          'end' => '01:00',
          'desc' => 'Event description 210-1',
        ),
      ),
    ),
  ),
)

Upvotes: 0

Expedito
Expedito

Reputation: 7795

The following does what you're looking for:

$out = array();
foreach ($arr as $key => $value){
    $cat = $value['cat'];
    $dur = $value['dur'];
    $type = $value['type'];
    $out[$cat][$dur][$type][] = $value['start'].', '.$value['end'].', '.$value['desc'];
}
print_r($out);

EDIT:

I modified my code because of a request to use the last category when the category is empty. So, here's my modified code:

    $out = array();
    $last_cat = '';
    foreach ($arr as $key => $value){
        $cat = $value['cat'];
        $dur = $value['dur'];
        $type = $value['type'];
        if (empty($cat)){
            $cat = $last_cat;
        } else {
            $last_cat = $cat;
        }
        $out[$cat][$dur][$type][] = $value['start'].', '.$value['end'].', '.$value['desc'];
    }
    print_r($out);

The ouput of this code is as follows:

[FIRST CAT] => Array(
        [10] => Array(
            [Type description 10] => Array(
                [0] => 00:00, 01:00, Event description 10-1
            )
        )
        [] => Array(
            [] => Array(
                    [0] => 01:00, 02:00, Event description 10-2
                    [1] => 02:00, 03:00, Event description 10-3
                    [2] => 01:00, 02:00, Event description 20-2
                    [3] => 01:00, 02:00, Event description 21-2
                )

            [Type description 21] => Array(
                    [0] => 00:00, 01:00, Event description 21-1
                )
        )
        [20] => Array(
            [Type description 20] => Array(
                    [0] => 00:00, 01:00, Event description 20-1
                )
        )
    )
[SECOND CAT] => Array(
        [10] => Array(
            [Type description 100] => Array(
                    [0] => 00:00, 01:00, Event description 100-1
                )
            )
        [] => Array(
            [] => Array(
                    [0] => 01:00, 02:00, Event description 100-2
                    [1] => 01:00, 02:00, Event description 200-2
                )
            [Type description 210] => Array(
                    [0] => 00:00, 01:00, Event description 210-1
                )
            )
        [20] => Array(
            [Type description 200] => Array(
                    [0] => 00:00, 01:00, Event description 200-1
                )
            )
    )

Upvotes: 1

Related Questions