Reputation:
I have the following array:
(
[25] => 1
[26] => 3
[10] => 2
[24] => 1
)
It was created using the array_count_values()
function in PHP.
Actual original array was something like this, before array_count_values...
Array
(
[0] => 26
[1] =>
[2] => 18
[3] => 28
[4] => 22
[5] => 21
[6] => 26
[7] =>
[8] =>
[9] =>
[10] =>
[11] =>
[12] =>
[13] =>
[14] =>
[15] =>
[16] =>
[17] =>
[18] =>
[19] =>
[20] =>
)
These are ages, so how can I group these into age groups?
Lets say I want the following age groups: <= 18
19-26
27-32
> 32
It is supposed to look:
(
[<= 18] => 1
[19-26] => 4
[27-32] => 2
[ > 32] => 1
)
Is there a ready function for this?
My solution:
1 tedious way would be to create variables of age groups. Than foreach and increase variable ++ for specific age group if key matches range ($min <= $value) && ($value <= $max)
...
Upvotes: 7
Views: 3376
Reputation: 165201
This sounds like a perfect case of map-reduce.
Map step: turn each age into one of the 4 ranges.
Reduce step: turn the range into a result array of ranges.
Let's try it out:
$map = function($age) {
if (!$age) {
return 'unknown';
} elseif ($age <= 18) {
return '<= 18';
} elseif ($age <= 26) {
return '19-26';
} elseif ($age <= 32) {
return '26-32';
}
return '> 32';
}
So basically here, we're just turning each age into a string representation of the range it represents. So now we'll have an array of ranges, so we need to reduce that out to a summary (add up all of those ranges):
And then the reduce function:
$reduce = function($result, $age) {
$result[$age]++;
return $result;
}
This is quite simple. If you wanted to support dynamic ranges, then you'd have some logic in there to check if the age is not set (and then initialize it to 0
)...
So now, putting it all together:
$array = array(12, 63, 24, 34, 12, 10, 19,); // Your ages
$data = array_map(function($age) {
if (!$age) {
return 'unknown';
} elseif ($age <= 18) {
return '<= 18';
} elseif ($age <= 26) {
return '19-26';
} elseif ($age <= 32) {
return '26-32';
}
return '> 32';
}, $array);
$result = array_reduce($data, function($result, $age) {
$result[$age]++;
return $result;
}, array('unknown' => 0, '<= 18' => 0, '19-26' => 0, '26-32' => 0, '> 32' => 0));
Which then yields:
array(4) {
["unknown"]=> int(0)
["<= 18"]=> int(3)
["19-26"]=> int(2)
["26-32"]=> int(0)
["> 32"]=> int(2)
}
Upvotes: 8
Reputation: 14921
After seeing all those solutions, i thought why not using Regex with the original array ?
$ages = array(25, 26, 10, 24, 10, 26, 26, 32, 32, 54, 84, 4, 18, 5, 98, 27);
$string = '#'. implode('#', $ages) . '#';
preg_match_all('/(#[0-9]#|#1[0-7]#)|(#1[8-9]#|#2[0-6]#)|(#2[7-9]#|#3[0-2]#)|(#3[3-9]#|#[4-9][0-9]#|#1[0-5][0-9]#)/', $string, $groups);
$age_ranges = array('0-17' => count(array_filter($groups[1])), '18-26' => count(array_filter($groups[2])), '27-32'
=> count(array_filter($groups[3])), '33-159' => count(array_filter($groups[4])));
print_r($age_ranges);
Output:
Array
(
[0-17] => 2
[18-26] => 3
[27-32] => 1
[33-159] => 2
)
Upvotes: 1
Reputation: 59699
Interesting problem. Here is my solution. First, define an array of age ranges:
$age_ranges = array(
array( 0, 18),
array( 19, 26),
array( 27, 32),
array( 33, 150) // I use 150 as the "max" age
);
Then, we have your array_count_values()
output:
$array_count_values = array( 25 => 1, 26 => 3, 10 => 2, 24 => 1); // From OP
Now, we create an array of all ages, where the keys are the age, and the values are the number of people with that age. It needs to be sorted by its keys for the next step.
$all_ages = $array_count_values + array_fill( 0, 150, 0);
ksort( $all_ages);
Finally, I loop over all the age ranges, slice off the age range from the $all_ages
array, and sum their values to produce an array of the age ranges, with its values corresponding to how many people fell into that age range.
$result = array();
foreach( $age_ranges as $range) {
list( $start, $end) = $range;
$result["$start-$end"] = array_sum( array_slice( $all_ages, $start, $end - $start + 1));
}
A print_r( $result);
yields the following output:
Array
(
[0-18] => 2
[19-26] => 5
[27-32] => 0
[33-150] => 0
)
Edit: Since you still have access to your original array, you can just calculate how many "unknowns" you had at the very end:
$result['unknown'] = count( array_filter( $original_array, function( $el) {
return empty( $el);
}));
Upvotes: 10
Reputation: 8223
If you wanted to just use the array you had before array_count_values()
, you could use
$ages = array(
0 => 26,
1 => 18,
2 => 28,
3 => 22,
4 => null,
5 => 21,
6 => 26,
7 => null);
create a blank array to fill up
$final = array(
'unknown' => 0,
'18' => 0,
'19_26' => 0,
'27_32' => 0,
'32' => 0,
);
array_walk($ages, function($age, $i, $final) {
if (!$age) { $final['unknown']++; return; }
if ($age <= 18) { $final['18']++; return; }
if ($age <= 26) { $final['19_26']++; return; }
if ($age <= 32) { $final['27_32']++; return; }
if ($age > 32) { $final['32']++; return; }
}, &$final);
output from $final:
array (size=5)
'unknown' => int 2
18 => int 1
'19_26' => int 4
'27_32' => int 1
32 => int 0
Upvotes: 1