Home > Software engineering >  Group and filter array rows based on multiple column values
Group and filter array rows based on multiple column values

Time:05-31

I have an array of rows containing room availability for different dates for a hotel. I need to filter the array to identify on which dates the hotel has no vacancy.

$array = [
    ["room_id" => "1", "date" => "01-07-2022", "available" => "A"],
    ["room_id" => "2", "date" => "01-07-2022", "available" => "A"],
    ["room_id" => "1", "date" => "02-07-2022", "available" => "A"],
    ["room_id" => "2", "date" => "02-07-2022", "available" => "U"],
    ["room_id" => "1", "date" => "03-07-2022", "available" => "U"],
    ["room_id" => "2", "date" => "03-07-2022", "available" => "U"],
    ["room_id" => "1", "date" => "04-07-2022", "available" => "U"],
]

01-07-2022 has only A entries, so that date has vacancies.

02-07-2022 has A and U entries, so that date has vacancies.

03-07-2022 has only U entries, so that date has NO vacancies.

04-07-2022 has only U, so that date has NO vacancies.

The expected result indicating dates with no vacancies::

[
   [
     "room_id" => "2",
     "date" => "03-07-2022"
     "available" => "U"
   ],
   [
     "room_id" => "1",
     "date" => "04-07-2022"
     "available" => "U"
   ],
]

I've tried the following:

foreach ($roomsArr as $room) {
 if ($room['availability'] === 'U') {
    $unavailable[] = $room;
 }
 else if ($room['availability'] === 'A') {
    $available[] = $room;
 }
}

foreach ($unavailable as $uv) {
    foreach ($available as $av) {
        if ($av['date'] === $uv['date']) {
            if ($av['availability'] === 'A' && $uv['availability'] === 'A') {

            }
            else if ($av['availability'] === 'A' && $uv['availability'] === 'U') {

            }
            else if ($av['availability'] === 'U' && $uv['availability'] === 'U') {
                $arr1[] = $av;
            }

        }
    }
}

CodePudding user response:

I'll show two approaches/snippets with different benefits.

First divide them into vacant and non-vacant arrays, then group the data in those two arrays by date so that you don't have duplicate entries per date.

After the data is separated and grouped, you merely need to compare the dates/keys where there are no vacancies against dates where there are vacancies. What is left over will be the dates when the hotel has no vacancies.

This is potentially easier to conceptualize, but will not be as performant as the second snippet because it executes two separate iterating techniques.

Code: (Demo)

$hasVacancy = [];
$hasNoVacancy = [];
foreach ($array as $row) {
    if ($row['available'] === 'A') {
        $hasVacancy[$row['date']] = $row;
    } else {
        $hasNoVacancy[$row['date']] = $row;
    }
}

var_export(
    array_values(array_diff_key($hasNoVacancy, $hasVacancy))
);

Output:

array (
  0 => 
  array (
    'room_id' => '2',
    'date' => '03-07-2022',
    'available' => 'U',
  ),
  1 => 
  array (
    'room_id' => '1',
    'date' => '04-07-2022',
    'available' => 'U',
  ),
)

More efficiently, you'll need to maintain a lookup array to ensure that a date has NO available rooms. I recommend this snippet if you can understand the logic. It only needs to traverse the input array one time.

Code: (Demo)

$hasVacancy = [];
$result = [];
foreach ($array as $row) {
    if ($row['available'] === 'A') {
        $hasVacancy[$row['date']] = $row; // store available as reference
        unset($result[$row['date']]);  // remove potentially stored unavailable row for date
    } elseif (!isset($hasVacancy[$row['date']])) {
        $result[$row['date']] = $row;  // is unavailable and no availables found
    }
}
var_export(array_values($result));
// same result as previous snippet
  • Related