0

I have a problem condensing a large list of dates.

The source code can be seen here https://onlinephp.io/c/d8277

Source code:

<?php
    function list_date($array = [])
    {
        $events = $array;

        for ($i = 0; $i < count($events); $i++)
        {
            $this_event = $events[$i];
            $this_start_date = strtotime($this_event["date"]);
            $this_end_date = strtotime($this_event["date"]);
            $this_start_month = date("M", $this_start_date);
            $this_end_month = date("M", $this_end_date);
            $this_start_year = date("Y", $this_start_date);
            $this_end_year = date("Y", $this_end_date);

            $last = ($i == count($events) - 1);
            if (!$last)
            {
                $next_event = $events[$i + 1];
                $next_start_date = strtotime($next_event["date"]);
                $next_end_date = strtotime($next_event["date"]);

                $next_start_month = date("M", $next_start_date);
                $next_end_month = date("M", $next_end_date);
                $next_start_year = date("Y", $next_start_date);
                $next_end_year = date("Y", $next_end_date);
            }

            if (($this_start_month != $this_end_month) ||
                ($this_start_year != $this_end_year))
            {
                echo date("j/m", $this_start_date);
                if ($this_start_year != $this_end_year)
                {
                    echo " ".date("Y", $this_start_date);
                }
                echo "-".date("j/m/Y", $this_end_year)." <br/>\n";
            }
            else
            {
                echo date("j", $this_start_date);
                if ($this_start_date != $this_end_date)
                {
                    echo "-".date("j", $this_end_date);
                }

                $newline = false;
                if ($last ||
                    ($this_start_month != $next_end_month))
                {
                    echo " ".date("m", $this_start_date);
                    $newline = true;
                }

                if ($last ||
                    ($this_start_year != $next_end_year) ||
                    ($next_start_month != $next_end_month))
                {
                    echo " ".date("Y", $this_start_date);
                    $newline = true;
                }

                if ($newline)
                {
                    echo " <br/>\n";
                }
                else
                {
                    echo ", ";
                }
            }
        }
    }
    
    $data = [
        [
            'name' => 'Event A',
            'date' => '2023-03-1'
        ],
        [
            'name' => 'Event B',
            'date' => '2023-03-2'
        ],
        [
            'name' => 'Event C',
            'date' => '2023-03-3'
        ],
        [
            'name' => 'Event D',
            'date' => '2023-03-7'
        ],
        [
            'name' => 'Event E',
            'date' => '2023-03-9'
        ],
    ];


    echo "Event Schedule " . list_date($data);

Current output: Event Schedule 1, 2, 3, 7, 9 03 2023

The output what I want: Event Schedule 1 - 3, 7, 9 / 03 / 2023

Thank you.

Alive to die - Anant
  • 70,531
  • 10
  • 51
  • 98

2 Answers2

1

Ok, so you can use DateTime class to get difference between 2 dates at ease.

  • Run a loop. If the current date is 1 day more than the previous date, they belong to the same group.

  • If the current date in loop when compared to the tracking variable we have is more than 1 day:

    • We add current tracking variables as a group to our resultant array
    • We start a new group from here by updating tracking variables with the current date.
  • In the end, we just print the output in your desired state.

Snippet:

<?php

$start_d = null; 
$curr_d = null;

$groups = [];

foreach($data as $d){
    $dd = DateTime::createFromFormat('!Y-m-d', $d['date']);
    if($start_d == null){
        $start_d = $curr_d = $dd;
    }else{
        $diff = $dd->diff($curr_d) ;
        if($diff->y === 0 && $diff->m === 0 && $diff->d === 1){
            $curr_d = $dd;   
        }else{
            $groups[] = [$start_d->format('d'), $curr_d->format('d')];
            $start_d = $curr_d = $dd;
        }
    }
}

$groups[] = [$start_d->format('d'), $curr_d->format('d')];

echo implode(" , ", array_map(fn($v) => implode(" - ", array_unique($v)), $groups)), " / ", $start_d->format('m'), " / ", $start_d->format('Y');

Online Demo

nice_dev
  • 17,053
  • 2
  • 21
  • 35
  • yeah... this code is almost perfect. But where do I change the group so that it can be separated by months and years if this is different. For example: https://3v4l.org/fim38 (March dan April) so the output will be 01 - 03 / 03 / 2023 , 07, 09 / 04 / 2023 @nice_dev – Toni Rhubanaga Mar 21 '23 at 11:39
  • @ToniRhubanaga What is the expected output for the input you gave? – nice_dev Mar 21 '23 at 11:54
1

One way to do it would be to initialize month-year-specific strings in the desired format, then as new days are encountered within the given month-year, use regex to inject/extend the substring in the middle of the formatted string.

Code: (Demo)

$result = [];
$last = [];
foreach ($array as ['date' => $date]) {
    sscanf($date, '%d-%d-%d', $y, $m, $d);
    $key = $y . $m; // group by year-month
    if (isset($last[$key])) {
        $lastPadded = sprintf('%02s', $last[$key]);
        $thisPadded = sprintf('%02s', $d);
        if (($d - 1) === $last[$key]) {
            $result[$key] = preg_replace("~-$lastPadded\b|\b$lastPadded\b\K~", "-$thisPadded", $result[$key]); // hyphenate
        } else {
            $result[$key] = preg_replace("~\b$lastPadded\b\K~", ", $thisPadded", $result[$key], 1); // comma-separate
        }
    } else {
        $result[$key] = sprintf('%02s / %02s / %d', $d, $m, $y); // push initial string for year-month
    }
    $last[$key] = $d;
}
echo implode(", ", $result);

Some of the convolution can be trimmed down if you actually desire unpadded day and month numbers.

mickmackusa
  • 43,625
  • 12
  • 83
  • 136