'Grouping of same values in an array

I have this array with year as its key and count as its value,

$dateRange = array([
   "1990" => 10,
   "1991" => 12,
   "1992" => 12,
   "1993" => 12,
   "1994" => 12,
   "1995" => 12,
   "1996" => 6
]);

and how can I make it look like this *(getting consecutive year/s with a count/value of 12 then count its total value)

$dateRange = ([
    "1990" => 10,
    "1991-1995" => 60,
    "1996" => 6
]);


Solution 1:[1]

I came up with the following solutions, it's not efficient with large data but will work. 1991-1995 will be string though.

      $dateRange = array([
        1990 => 10,
        1991 => 12,
        1992 => 12,
        1993 => 12,
        1994 => 12,
        1995 => 12,
        1996 => 6
      ]);
  
      dump($dateRange[0]);
      
      $first = reset($dateRange[0]);
      $last  = end($dateRange[0]);
      $newArr = array([]);
      $sum = 0;
      $firstRange = null;
      $lastRange = null;
      foreach($dateRange[0] as $key => $item){
        if($item == $first){
            $newArr[0][$key] = $item;
        }
        elseif($item == $last){
            $newArr[0][$firstRange.'-'.$lastRange] = $sum;
            $newArr[0][$key] = $item;
        }
        else{
            if($firstRange == null){
                $firstRange = $key;
            }
            $lastRange = $key;
            $sum += $item;
        }
      }
      dd($newArr[0]);

Solution 2:[2]

Loop through each individual dataset(subarray) and

  • Sort the keys numerically.

  • Compare every current key with previous key to check if they are consecutive.

    If prev_key + 1 == curr_key, then they are consecutive, else they are not.

  • Check the same for their values' equality as well.

  • In either step 2 or 3, if any of the conditions don't match, add the current range and it's sum to the result and start a new range from this index. Likewise, collect all the results.

Snippet:

<?php

$res = [];

foreach($dateRange as $k => $v){
    $temp = [];
    $keys = array_keys($v);
    sort($keys, SORT_NUMERIC);
    $prev = 0;
    $sum = 0;
    for($i = 0; $i < count($keys); ++$i){
        if($i > 0 && ($keys[ $i - 1 ] + 1 != $keys[ $i ] || $v[ $keys[ $i - 1 ] ] !== $v[ $keys[ $i ] ])){
            if($prev == $i - 1) $temp[ $keys[ $prev ]] = $sum;
            else $temp[ $keys[$prev] . "-" . $keys[$i - 1]] = $sum;
            $sum = 0;
            $prev = $i;
        }
        
        $sum += $v[$keys[ $i ]];
        if($i === count($keys) - 1){
            if($prev == $i) $temp[ $keys[ $prev ]] = $sum;
            else $temp[ $keys[$prev] . "-" . $keys[ $i ]] = $sum;
        }
    }
    $res[] = $temp;
}


print_r($res);

Online Demo

Solution 3:[3]

Here is a more Laravel friendly solution using Framework collection methods.
Using Collection is more readable and more understandable.

 $dateRange = [
            [
                "1990" => 10,
                "1991" => 12,
                "1992" => 12,
                "1993" => 12,
                "1994" => 12,
                "1995" => 12,
                "1996" => 6,
            ],
        ];
        collect($dateRange)->map(function ($range)
        {
            return collect($range)->groupBy(function ($item)
            {
                return $item;
            }, true)->mapWithKeys(function (Collection $item)
            {
                $sorted = $item->sortKeys();
                $keys = $sorted->keys();
                $firstYear = $keys->first();
                $lastYear = $keys->last();
                $sum = $item->sum();

               return [($firstYear != $lastYear ? $firstYear.'-'.$lastYear : $firstYear) => $sum];

            });

        })->dd();

Output:

  array:1 [?
  0 => Illuminate\Support\Collection {#2259 ?
    #items: array:3 [?
      1990 => 10
      "1991-1995" => 60
      1996 => 6
    ]
    #escapeWhenCastingToString: false
  }
]

Solution 4:[4]

$dateRange = [
    '1990' => 10,
    '1991' => 12,
    '1992' => 12,
    '1993' => 12,
    '1994' => 12,
    '1995' => 12,
    '1996' => 6
];

ksort($dateRange);

$previousValue = '';

$result = [];

array_walk(
    $dateRange,
    function (int $value, string $key) use (&$previousValue, &$result) {
      if ($previousValue === $value) {
        $lastKey = array_key_last($result);
        $result[substr($lastKey, 0, 4) . '-' . $key] = $result[$lastKey] + $value;
        unset($result[$lastKey]);
      } else {
        $result[$key] = $value;
      }
      $previousValue = $value;
    },
    []
);

print_r($result);

Solution 5:[5]

You have an array of arrays here. You could just do $dateRange = [...]; but I'll assume this is what you intended.

$dateRange = array([
   "1990" => 10,
   "1991" => 12,
   "1992" => 12,
   "1993" => 12,
   "1994" => 12,
   "1995" => 12,
   "1996" => 6
]);

Using Collection methods, it should be easy.

$collection = collect($dateRange[0]);
$keys = $collection->keys();

$newKeys = [
    $keys->shift(),                              // remove and return first key of $keys (1990)
    $keys->pop(),                                // remove and return last key of $keys (1996)
    "{$keys->first()}-{$keys->last()}",          // concat first key and last key of $keys ("1991-1995")
];
                                                 // $newKeys is now equal to [1990, "1991-1995", 1996]

$newValues = [
    0 => $collection->pull($newKeys[0]),         // remove and return element 1990 from collection (10)
    2 => $collection->pull($newKeys[1]),         // remove and return element 1996 from collection (6)
    1 => $collection->sum(),                     // return sum of remaining elements in collection (60)
];

ksort($newValues);                               // sort $newValues by its key (this is why I specified them in the order 0, 2, 1).
                                                 // $newValues is equal to [10, 60, 6]

$newArray = array_combine($newKeys, $newValues); // create a new array from $newKeys and $newValues

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Anshul Kumar
Solution 2 nice_dev
Solution 3 ml59
Solution 4 lukas.j
Solution 5 IGP