'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);
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 |
