通常はデータベース上でSQLを使ってgroup byをすれば十分ですが、下記のような場合プログラム側で実施するのもありです。
- DBから取得したデータから別の複数の集計(group by)結果を得ることができる
- DBのgroup byの実行に時間がかかり、何度も似たような集計をDBに計算させるのは実行コスト(時間、CPU負荷)が高い
今回は汎用的にPHP側でgroup byを実行できるコードを書いてみました。
任意の複数フィールドで集計できるようにするため、多少コードが複雑になっています。
またkeyのencode/decode部分の処理で多少無駄があります。
<?php function groupBy(array $rows, array $groupByFields, callable $aggregate) { $groupByFieldsAsKey = array_flip($groupByFields); $map = []; foreach($rows as $row) { // キーになるならどんな関数でもOK。ここではjson_encodeを採用 $key = json_encode(array_intersect_key($row, $groupByFieldsAsKey)); $aggregatedRow = &$map[$key] ?? []; $aggregate($aggregatedRow, $row); } $result = []; foreach ($map as $key => &$aggregatedRow) { // キーをデシリアライズして、フィールドと値を集計結果の行にコピー $keyValue = json_decode($key, true); foreach($groupByFields as $groupByField) { $aggregatedRow[$groupByField] = $keyValue[$groupByField]; } $result[] = $aggregatedRow; } return $result; }
下記は使用例です。$rowsを'country'、'campany'の2つのフィールドでgroup byしています。
$rows = [ ['id' => 1, 'campany' => 'Amazon', 'country' => 'USA', 'name' => 'Jeff Bezos' , 'net_worth' => 113], ['id' => 2, 'campany' => 'Microsoft', 'country' => 'USA', 'name' => 'Bill Gates' , 'net_worth' => 98], ['id' => 3, 'campany' => 'LVMH', 'country' => 'France', 'name' => 'Bernard Arnault' , 'net_worth' => 76], ['id' => 4, 'campany' => 'Berkshire Hathaway', 'country' => 'USA', 'name' => 'Warren Buffett' , 'net_worth' => 68], ['id' => 5, 'campany' => 'Oracle Corporation', 'country' => 'USA', 'name' => 'Larry Ellison' , 'net_worth' => 59], ['id' => 6, 'campany' => 'Inditex, Zara', 'country' => 'Spain', 'name' => 'Amancio Ortega' , 'net_worth' => 55], ['id' => 7, 'campany' => 'Facebook', 'country' => 'USA', 'name' => 'Mark Zuckerberg' , 'net_worth' => 54], ['id' => 8, 'campany' => 'Walmart', 'country' => 'USA', 'name' => 'Jim Walton' , 'net_worth' => 54], ['id' => 9, 'campany' => 'Walmart', 'country' => 'USA', 'name' => 'Jeff Bezos' , 'net_worth' => 54], ['id' => 10, 'campany' => 'Walmart', 'country' => 'USA', 'name' => 'S. Robson Walton' , 'net_worth' => 54], ]; $groupByFields = ['country', 'campany']; $calculateTotalNetWorth = function(&$aggregatedRow, $row) { $aggregatedRow['total_net_worth'] = ($aggregatedRow['total_net_worth'] ?? 0) + $row['net_worth']; }; $result = groupBy($rows, $groupByFields, $calculateTotalNetWorth); echo json_encode($result, JSON_PRETTY_PRINT); /* 出力は下記のようになります。 [ { "total_net_worth": 113, "country": "USA", "campany": "Amazon" }, { "total_net_worth": 98, "country": "USA", "campany": "Microsoft" }, { "total_net_worth": 76, "country": "France", "campany": "LVMH" }, { "total_net_worth": 68, "country": "USA", "campany": "Berkshire Hathaway" }, { "total_net_worth": 59, "country": "USA", "campany": "Oracle Corporation" }, { "total_net_worth": 55, "country": "Spain", "campany": "Inditex, Zara" }, { "total_net_worth": 54, "country": "USA", "campany": "Facebook" }, { "total_net_worth": 162, "country": "USA", "campany": "Walmart" } ] */
コメント