This is K.I.S.S, but how about some error and misuse control? I am wishing it were .NET.
<?php
function array_group_by($arr, $key_selector) {
$result = array();
foreach($arr as $i){
$key = $key_selector($i);
if(!array_key_exists($key, $result)) {
$result[$key] = array();
}
$result[$key][] = $i;
}
return $result;
}
$data = array(
array(1, "Andy", "PHP"),
array(1, "Andy", "C#"),
array(2, "Josh", "C#"),
array(2, "Josh", "ASP"),
array(1, "Andy", "SQL"),
array(3, "Steve", "SQL"),
);
$grouped = array_group_by($data, function($i){ return $i[0]; });
var_dump($grouped);
?>
1 Answer 1
- Using the
[]
syntax for adding array items, allows PHP to create the array as necessary. Thus you can scrap theif
statement for a modest performance increase off 10 to 15% in my own testing. (Unlike if you usedarray_push()
which would throw a warning instead. The unused
$group
variable appears to be a mistake, as the function appears to fulfill it's purpose without it. Code reduced to:function array_group_by($arr, $key_selector) { $result = array(); foreach ($arr as $i) { $key = $key_selector($i); $result[$key][] = $i; } return $result; }
- At this point, the
$key
could also be left out, but it's almost free in terms of performance, and increases readability, so I left it there. I'm not sure how much misuse and error handling you need, the function is pretty simple. I would stick with type-hinting the arguments.
array
is available since PHP 5.1,callable
since 5.4. The signature would becomefunction array_group_by(array $arr, callable $key_selector);
Then PHP will at run-time throw a fatal error at anyone trying to call the function with incorrect parameters. Generally I think that's good, but in this particular case, it introduces a problem.
array('class_A', 'method_B')
, is a valid callable, that won't work with with the fast$function($arg);
. For any valid callable to get executed, you need to usecall_user_func()
instead, which is a bit slower. In my testing however, the removal of theif
has bigger impact than the use ofcall_user_func()
, so if this isn't performance critical, stick with callable andcall_user_func()
.
Final code:
function array_group_by(array $arr, callable $key_selector) {
$result = array();
foreach ($arr as $i) {
$key = call_user_func($key_selector, $i);
$result[$key][] = $i;
}
return $result;
}
-
\$\begingroup\$ Type hinting... Yeah I am rusty on PHP. This is a great answer. I'll give others a chance to bite before marking it as the answer. \$\endgroup\$AndyClaw– AndyClaw2013年03月14日 19:06:41 +00:00Commented Mar 14, 2013 at 19:06
-
\$\begingroup\$ Glad I could help. :) \$\endgroup\$Letharion– Letharion2013年03月14日 19:07:59 +00:00Commented Mar 14, 2013 at 19:07
-
\$\begingroup\$ How about an array walk instead of a foreach? Will that just make it less readable? \$\endgroup\$AndyClaw– AndyClaw2013年03月14日 19:09:21 +00:00Commented Mar 14, 2013 at 19:09
-
\$\begingroup\$ I pondered various native array functions for a moment, but I think most (all?) of them want to act directly on the individual elements of the array, which makes them unsuitable to use when you want out data with a different structure than the in data. I could be wrong on this though. \$\endgroup\$Letharion– Letharion2013年03月14日 19:14:37 +00:00Commented Mar 14, 2013 at 19:14
-
\$\begingroup\$ I changed "callable" to "closure" \$\endgroup\$AndyClaw– AndyClaw2013年03月14日 19:57:06 +00:00Commented Mar 14, 2013 at 19:57