0

I have a date range 2023-11-01 to 2024-01-04 and some date have different MinStay value.

This is my array of date range

$input = [
  [ 'date' => '2023-11-01', 'MinStay' => 1 ],
  [ 'date' => '2023-11-02', 'MinStay' => 1 ],
  [ 'date' => '2023-11-03', 'MinStay' => 1 ],
  [ 'date' => '2023-11-04', 'MinStay' => 2 ],
  [ 'date' => '2023-11-05', 'MinStay' => 2 ],
  [ 'date' => '2023-11-06', 'MinStay' => 2 ],
  [ 'date' => '2023-12-10', 'MinStay' => 1 ],
  [ 'date' => '2023-12-11', 'MinStay' => 1 ],
  [ 'date' => '2023-12-12', 'MinStay' => 3 ],
  [ 'date' => '2023-12-13', 'MinStay' => 2 ],
  [ 'date' => '2023-12-14', 'MinStay' => 2 ],
  [ 'date' => '2024-01-01', 'MinStay' => 4 ],
  [ 'date' => '2024-01-02', 'MinStay' => 4 ],
  [ 'date' => '2024-01-03', 'MinStay' => 4 ],
  [ 'date' => '2024-01-04', 'MinStay' => 4 ],
];

And I want to

$output = [
  [ 'dateForm' => '2023-11-01', 'dateTo' => '2023-11-03', 'MinStay' => 1 ],
  [ 'dateForm' => '2023-11-04', 'dateTo' => '2023-11-06', 'MinStay' => 2 ],
  [ 'dateForm' => '2023-12-10', 'dateTo' => '2023-12-11', 'MinStay' => 1 ],
  [ 'dateForm' => '2023-12-12', 'dateTo' => '2023-12-12', 'MinStay' => 3 ],
  [ 'dateForm' => '2023-12-13', 'dateTo' => '2023-12-14', 'MinStay' => 2 ],
  [ 'dateForm' => '2024-01-01', 'dateTo' => '2024-01-04', 'MinStay' => 4 ],
];

How can I solve this issue.

I'm try using this array but it's not happening.

$arr = [];
foreach ($input as $date) {
    if ($date['MinStay'] == 1) {
        $arr[] = [
            'dateFrom' => $date['date'],
            'dateTo' => $date['date'],
            'MinStay' => $date['MinStay'],
        ];
    }
}
4
  • Is dateForm supposed to be dateFrom? Commented Oct 1, 2024 at 19:59
  • Why is your code only processing the items with minStay == 1? Commented Oct 1, 2024 at 20:00
  • Topically related: From an array, group element if dates are consecutive in PHP Commented Oct 2, 2024 at 4:30
  • @Barmar Yes its dateFrom Commented Oct 2, 2024 at 4:37

2 Answers 2

1

For maximum elegance, push reference variables into the result array and only update the current reference.

Your input data was insufficiently challenging to expose solutions which do not factor contiguous dates. I've extended the sample input.

$input = [
  [ 'date' => '2023-11-01', 'MinStay' => 1 ],
  [ 'date' => '2023-11-02', 'MinStay' => 1 ],
  [ 'date' => '2023-11-03', 'MinStay' => 1 ],
  [ 'date' => '2023-11-04', 'MinStay' => 2 ],
  [ 'date' => '2023-11-05', 'MinStay' => 2 ],
  [ 'date' => '2023-11-06', 'MinStay' => 2 ],
  [ 'date' => '2023-12-10', 'MinStay' => 1 ],
  [ 'date' => '2023-12-11', 'MinStay' => 1 ],
  [ 'date' => '2023-12-12', 'MinStay' => 3 ],
  [ 'date' => '2023-12-13', 'MinStay' => 2 ],
  [ 'date' => '2023-12-14', 'MinStay' => 2 ],
  [ 'date' => '2023-12-24', 'MinStay' => 2 ],  // <-- added challenge where same minstay is not on contiguous date
  [ 'date' => '2024-01-01', 'MinStay' => 4 ],
  [ 'date' => '2024-01-02', 'MinStay' => 4 ],
  [ 'date' => '2024-01-03', 'MinStay' => 4 ],
  [ 'date' => '2024-01-04', 'MinStay' => 4 ],
];

Code: (Demo)

$result = [];
$lastMinStay = null;
foreach ($input as ['date' => $d, 'MinStay' => $ms]) {
    if ($ms !== $lastMinStay || $d !== date('Y-m-d', strtotime("{$ref['dateTo']} +1 day"))) {
        unset($ref);
        $ref = ['dateFrom' => $d, 'dateTo' => $d, 'MinStay' => $ms];
        $result[] =& $ref;
        $lastMinStay = $ms;
        continue;
    }
    $ref['dateTo'] = $d;
}
var_export($result);

Output:

array (
  0 => 
  array (
    'dateFrom' => '2023-11-01',
    'dateTo' => '2023-11-03',
    'MinStay' => 1,
  ),
  1 => 
  array (
    'dateFrom' => '2023-11-04',
    'dateTo' => '2023-11-06',
    'MinStay' => 2,
  ),
  2 => 
  array (
    'dateFrom' => '2023-12-10',
    'dateTo' => '2023-12-11',
    'MinStay' => 1,
  ),
  3 => 
  array (
    'dateFrom' => '2023-12-12',
    'dateTo' => '2023-12-12',
    'MinStay' => 3,
  ),
  4 => 
  array (
    'dateFrom' => '2023-12-13',
    'dateTo' => '2023-12-14',
    'MinStay' => 2,
  ),
  5 => 
  array (
    'dateFrom' => '2023-12-24',
    'dateTo' => '2023-12-24',
    'MinStay' => 2,
  ),
  6 => 
  array (
    'dateFrom' => '2024-01-01',
    'dateTo' => '2024-01-04',
    'MinStay' => 4,
  ),
)
Sign up to request clarification or add additional context in comments.

2 Comments

If you do not need the contiguous date checks (because your application's logic prevents this problem), then just remove || $d !== date('Y-m-d', strtotime("{$ref['dateTo']} +1 day")) from this answer.
Thank you @mickmackusa your answer also good and run smoothly.
0

Firstly, loop through the array, keep track of the current date range, and compare the MinStay value with the previous one

If they are the same, extend the range, otherwise, push the current range to the output and start a new one.

$input = [
      ['date' => '2023-11-01', 'MinStay' => 1],
      ['date' => '2023-11-02', 'MinStay' => 1],
      ['date' => '2023-11-03', 'MinStay' => 1],
      ['date' => '2023-11-04', 'MinStay' => 2],
      ['date' => '2023-11-05', 'MinStay' => 2],
      ['date' => '2023-11-06', 'MinStay' => 2],
      ['date' => '2023-12-10', 'MinStay' => 1],
      ['date' => '2023-12-11', 'MinStay' => 1],
      ['date' => '2023-12-12', 'MinStay' => 3],
      ['date' => '2023-12-13', 'MinStay' => 2],
      ['date' => '2023-12-14', 'MinStay' => 2],
      ['date' => '2024-01-01', 'MinStay' => 4],
      ['date' => '2024-01-02', 'MinStay' => 4],
      ['date' => '2024-01-03', 'MinStay' => 4],
      ['date' => '2024-01-04', 'MinStay' => 4],
];

$output = [];
$currentRange = null; // Will store the current range of dates

foreach ($input as $date) {
    // If currentRange is empty, start a new range
    if ($currentRange === null) {
        $currentRange = [
            'dateForm' => $date['date'],
            'dateTo' => $date['date'],
            'MinStay' => $date['MinStay'],
        ];
    } else {
        // Check if MinStay matches the current range's MinStay
        if ($date['MinStay'] === $currentRange['MinStay']) {
            // Extend the date range
            $currentRange['dateTo'] = $date['date'];
        } else {
            // If MinStay changes, push the current range to the output
            $output[] = $currentRange;

            // Start a new range
            $currentRange = [
                'dateForm' => $date['date'],
                'dateTo' => $date['date'],
                'MinStay' => $date['MinStay'],
            ];
        }
    }
}

// Add the last range to the output
if ($currentRange !== null) {
    $output[] = $currentRange;
}

// Output the result
print_r($output);

1 Comment

This answer proves to be incorrect when given more challenging input data -- where neighbouring, non-contiguous dates share the same MinStay. 3v4l.org/IJ30Z

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.