I want to take a list of years and collapse them down to hyphenated ranges. It works better for small screen on mobile devices, displaying “2009-2016” instead of “2009, 2010, 2011, 2012, etc.”
e.g., there are 3 transforms on the array of integers [2015, 2017, 2006, 2010, 2009, 2018, 2008]
- sort the years
[2006, 2008, 2009, 2010, 2015, 2017, 2018]
- group into subsequent years
[[2006], [2008, 2009, 2010], [2015], [2017, 2018]]
- collapse into ranges
[‘2006’, ‘2008-2010’, ‘2015’, ‘2017-2018’]
For a 0-indexed array, an integer key groupBy()
finding the neighboring difference of 1 solves this problem. It’s the magic of maths.
I’ve implemented this solution using a collection pipeline. Third-party PHP packages illuminate/support (tightenco/collect is a Collection
-only subset) or dusank/knapsack will do the trick. They give an expressive, functional approach to transforming PHP arrays.
$years = [2015, 2017, 2006, 2010, 2009, 2018, 2008];
collect($years)
->sort()
->unique()
->values()
->groupBy(function ($year, $key) {
return $key - $year;
})
->map(function ($years) {
return $years->bookends()->implode('-');
})
->values()
->all();
// ['2006', '2008-2010', '2015', '2017-2018']
bookends()
is an additional function added to Laravel’s macroable Collection
class.
use Illuminate\Support\Collection;
Collection::macro('bookends', function () {
$items = [];
if ($this->isNotEmpty()) {
$items[] = $this->first();
}
if ($this->count() > 1) {
$items[] = $this->last();
}
return new static($items);
});
Without a for
loop in sight, I’ve used this solution to show what years a football player laced up the boots in their career.