Search code examples
phparraysmultidimensional-arraygrouping

Group subsets of data in 3-level array by identifying column


I've this type of array in PHP:

Array(
     [100] => Array(
          [1] => Array (
               [AVA_Date] => 2019-04-18
               [ROO_Id] => 100
               [RAT_Id] => 9
          )
          [2] => Array (
               [AVA_Date] => 2019-04-20
               [ROO_Id] => 100
               [RAT_Id] => 10
          )
          [4] => Array (
               [AVA_Date] => 2019-04-21
               [ROO_Id] => 100
               [RAT_Id] => 10
          )
          [7] => Array (
               [AVA_Date] => 2019-04-22
               [ROO_Id] => 100
               [RAT_Id] => 9
          )
     )
)

I would like to merge items on ROO_Id and RAT_Id.

Then, for the AVA_Date, I need to list them under a new array in the current array.

So, the desired output is:

Array(
     [100] => Array(
          [0] => Array (
               [AVA_Date] => Array (
                    [0] => 2019-04-18
                    [1] => 2019-04-22
               )
               [ROO_Id] => 100
               [RAT_Id] => 9
          )
          [1] => Array (
               [AVA_Date] => Array (
                    [0] => 2019-04-20
                    [1] => 2019-04-21
               )
               [ROO_Id] => 100
               [RAT_Id] => 10
          )
     )
)

Here what I have tried:

$newArrOtherRooms  = array_reduce($newArr, function($acc, $val) {
    $room = array_search($val['ROO_Id'], array_column($acc, 'ROO_Id'));
    $rate = array_search($val['RAT_Id'], array_column($acc, 'RAT_Id'));
    if($rate == $room && $room > -1) {
        array_push($acc[$room]['AVA_Date'], $val['AVA_Date']);
    }
    else {
        $new_arr = $val;
        $new_arr['AVA_Date'] = [$val['AVA_Date']];
        array_push($acc, $new_arr);
    }
    return $acc;
},[]);

But it doesn't work like I want.


Solution

  • There are a couple of issues with your code. Firstly, you need to wrap the array_reduce with a foreach over the outer level of $newArr. Secondly, your call to array_search doesn't consider the fact that a ROO_Id or RAT_Id value might exist more than once in the array, as it only returns the first key at which it finds the value. To work around this, you can use array_keys to get an array of key values for each ROO_Id and RAT_Id value, and then take the intersection of those two arrays using array_intersect to see if both are present in the same element. If so, you update that element, otherwise you create a new one:

    foreach ($newArr as $key => $array) {
        $newArrOtherRooms[$key]  = array_reduce($array, function($acc, $val) {
            $room = array_keys(array_column($acc, 'ROO_Id'), $val['ROO_Id']);
            $rate = array_keys(array_column($acc, 'RAT_Id'), $val['RAT_Id']);
            $common = array_intersect($room, $rate);
            if(!empty($common)) {
                array_push($acc[current($common)]['AVA_Date'], $val['AVA_Date']);
            }
            else {
                $new_arr = $val;
                $new_arr['AVA_Date'] = [$val['AVA_Date']];
                array_push($acc, $new_arr);
            }
            return $acc;
        },[]);
    }
    print_r($newArrOtherRooms);
    

    Output:

    Array(
         [100] => Array(
              [0] => Array (
                   [AVA_Date] => Array (
                        [0] => 2019-04-18
                        [1] => 2019-04-22
                   )
                   [ROO_Id] => 100
                   [RAT_Id] => 9
              )
              [1] => Array (
                   [AVA_Date] => Array (
                        [0] => 2019-04-20
                        [1] => 2019-04-21
                   )
                   [ROO_Id] => 100
                   [RAT_Id] => 10
              )
         )
    )
    

    Demo on 3v4l.org