Asked  7 Months ago    Answers:  5   Viewed   44 times

I have arrays of dates Y-m-d format that may be any combination of ten set dates that are one day apart.

e.g. Here is the full set:

2011-01-01, 2011-01-02, 2011-01-03, 2011-01-04, 2011-01-05, 2011-01-06, 2011-01-07, 2011-01-08, 2011-01-09, 2011-01-10

The arrays that are created from that set can be any combination of dates— all of them, one of them, some consecutive, all consecutive, etc.

I currently have them printing pretty much as they're returned. e.g. here's a possible result:

2011-01-02

2011-01-03

2011-01-04

2011-01-08

(what's actually printed is more like "Friday, Jan. 2…", but we'll stick with the simple date string)

I'd like to condense it so that if there are three or more consecutive days, those become a range, e.g the above example would become:

2011-01-02 to 2011-01-04

2011-01-08

which would eventually become:

Sunday, Jan. 2 - Tuesday, Jan. 4

Saturday Jan. 8

Is there a way to loop through and check the time difference, create a start time and end time for the range(s), and then gather up the stragglers?

 Answers

57

Bit of a quick answer so sorry about the lack of implementation but assuming you are using 5.3 and the dates are ordered chronologically, you could convert each date to a DateTime object (if they aren't already) and then iterate over the array using DateTime::diff() to generate a DateInterval object which you could use to compare the current date in the iteration with the last. You could group your consecutive dates into sub arrays and use shift() and pop() to get the first and last days in that sub array.

EDIT

I had a think about this. Pretty rough and ready implementation follows, but it should work:

// assuming a chronologically
// ordered array of DateTime objects 

$dates = array(
    new DateTime('2010-12-30'), 
    new DateTime('2011-01-01'), 
    new DateTime('2011-01-02'), 
    new DateTime('2011-01-03'), 
    new DateTime('2011-01-06'), 
    new DateTime('2011-01-07'), 
    new DateTime('2011-01-10'),
);

// process the array

$lastDate = null;
$ranges = array();
$currentRange = array();

foreach ($dates as $date) {    

    if (null === $lastDate) {
        $currentRange[] = $date;
    } else {

        // get the DateInterval object
        $interval = $date->diff($lastDate);

        // DateInterval has properties for 
        // days, weeks. months etc. You should 
        // implement some more robust conditions here to 
        // make sure all you're not getting false matches
        // for diffs like a month and a day, a year and 
        // a day and so on...

        if ($interval->days === 1) {
            // add this date to the current range
            $currentRange[] = $date;    
        } else {
            // store the old range and start anew
            $ranges[] = $currentRange;
            $currentRange = array($date);
        }
    }

    // end of iteration... 
    // this date is now the last date     
    $lastDate = $date;
}

// messy... 
$ranges[] = $currentRange;

// print dates

foreach ($ranges as $range) {

    // there'll always be one array element, so 
    // shift that off and create a string from the date object 
    $startDate = array_shift($range);
    $str = sprintf('%s', $startDate->format('D j M'));

    // if there are still elements in $range
    // then this is a range. pop off the last 
    // element, do the same as above and concatenate
    if (count($range)) {
        $endDate = array_pop($range);
        $str .= sprintf(' to %s', $endDate->format('D j M'));
    }

    echo "<p>$str</p>";
}

Outputs:

Thu 30 Dec
Sat 1 Jan to Mon 3 Jan
Thu 6 Jan to Fri 7 Jan
Mon 10 Jan
Wednesday, March 31, 2021
 
CodeCaster
answered 7 Months ago
96

You could also take a look at the DatePeriod class:

$period = new DatePeriod(
     new DateTime('2010-10-01'),
     new DateInterval('P1D'),
     new DateTime('2010-10-05')
);

Which should get you an array with DateTime objects.

To iterate

foreach ($period as $key => $value) {
    //$value->format('Y-m-d')       
}
Wednesday, March 31, 2021
 
CAMason
answered 7 Months ago
24

You need to call DateInterval::format() to display that difference as a string.

echo $diff->format('%d days');

See the manual for all of the available formatting options.

Wednesday, July 7, 2021
 
mattltm
answered 4 Months ago
31

You just need to use your vals dict and keep keys from aDict with values that have a count == 1 in vals then calling sorted to get a sorted output list:

def existsOnce3(aDict):  
    vals = {}
    # create dict to sum all value counts
    for i in aDict.values():
        vals.setdefault(i,0)
        vals[i] += 1   
    # use each v/val from aDict as the key to vals
    # keeping each k/key from aDict if the count is 1
    return sorted(k for k, v in aDict.items() if vals[v] == 1)

Using a collections.Counter dict to do the counting just call Counter on your values then apply the same logic, just keep each k that has a v count == 1 from the Counter dict:

from collections import Counter
cn = Counter(aDict.values())
print(sorted(k for k,v in aDict.items() if cn[v] == 1))
Friday, August 27, 2021
 
123r789
answered 2 Months ago
68

Change

setTimeout(lhc(), 5000);

to

setTimeout(lhc, 5000);

You're calling the function directly without a timeout when you add the parenthesis, and calling the function right away inside the same function quickly becomes an endless loop that fills up the stack

Monday, September 20, 2021
 
Smitha
answered 1 Month ago
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :