例えば、こういう食べ物の連想配列を
<?php
$foods = [
['name' => 'Apple', 'category' => 'fruits'],
['name' => 'Strawberry', 'category' => 'fruits'],
['name' => 'Tomato', 'category' => 'vegetables'],
['name' => 'Carot', 'category' => 'vegetables'],
['name' => 'water', 'category' => 'drink'],
['name' => 'beer', 'category' => 'drink'],
];
?>
カテゴリごとにグループ化して、こんな感じにしたい:
<?php
$expected = [
'fruits' => [
['name' => 'Apple', 'category' => 'fruits'],
['name' => 'Strawberry', 'category' => 'fruits'],
],
'vegetables' => [
['name' => 'Tomato', 'category' => 'vegetables'],
['name' => 'Carot', 'category' => 'vegetables'],
],
'drink' => [
['name' => 'water', 'category' => 'drink'],
['name' => 'beer', 'category' => 'drink'],
],
];
?>
実装案1: foreachを使ったよくある実装
<?php
function array_group_by(array $items, $keyName)
{
$groups = [];
foreach ($items as $item) {
$key = $item[$keyName];
if (array_key_exists($key, $groups)) {
$groups[$key][] = $item;
} else {
$groups[$key] = [$item];
}
}
return $groups;
}
assert(array_group_by($foods, 'category') === $expected);
?>
更に短縮すると:
function group_by(array $table, string $key): array
{
$groups = [];
foreach ($table as $row) {
$groups[$row[$key]][] = $row;
}
return $groups;
}
実装案2: イミュータブル風
<?php
function array_group_by2(array $items, $keyName)
{
$keys = array_unique(array_column($items, $keyName));
return array_combine($keys, array_map(function($key) use ($items, $keyName) {
return array_values(array_filter($items, function ($item) use ($key, $keyName) {
return $item[$keyName] == $key;
}));
}, $keys));
}
assert(array_group_by2($foods, 'category') === $expected);
?>
変数スコープとコレクションのAPI、辛すぎ・・・。やはりPHPはforeach
を使うが良さそう。
実装案3: 先に入れ物を作ってforeachでミューテート
<?php
function array_group_by3(array $items, $keyName)
{
$groups = array_fill_keys(array_column($items, $keyName), []);
foreach ($items as $item) {
$groups[$item[$keyName]][] = $item;
}
return $groups;
}
assert(array_group_by3($foods, 'category') === $expected);
?>
実装案1と比べて、if-elseが無くなった。まずまずと言った感じ・・・?
実装案4: array_reduce
function group_by(array $table, string $key): array
{
return array_reduce($table, function (array $groups, array $row) use ($key) {
$groups[$row[$key]][] = $row;
return $groups;
}, []);
}