PHP

【PHP】連想配列の特定のキーの値の合計をいろんな方法で取得してみる

More than 1 year has passed since last update.

タイトルの通りですが、DBからデータを取得したり、CSVファイルを読み込んだときにできる下のような連想配列の特定のキーの合計を求めたいってときにいつも同じやり方でやっていたので、違う方法がないか調べてみました。

// quantityの合計を求めたい。。。
$arrData = array(
                array('name' => 'A', 'quantity' => 0),
                array('name' => 'B', 'quantity' => 2),
                array('name' => 'C', 'quantity' => 1),
                array('name' => 'D', 'quantity' => 4),
                array('name' => 'E', 'quantity' => 10),
            );

foreachでループする

まず合計値を初期化(0を代入)し、foreachでループさせ特定のキーの値を加算させる方法です。最初に思いつくやり方かと思います。(今までずっとこれでやってました)

$f_total = 0;
foreach ($arrData as $data) {
    $f_total += $data['quantity'];
}
echo "foreach:total = ".$f_total.PHP_EOL; // foreach:total = 17

array_reduceとクロージャを組み合わせる

array_reduceの第1引数にループさせる配列、第2引数に処理する関数をしてします。処理する関数は関数化するまでもないので、ちょっとカッコよくクロージャで実装します。
array_reduce
クロージャ(無名関数)

$ar_total = array_reduce($arrData, function($carry, $item){
            return $carry += $item['quantity'];
        });
echo "array_reduce:total = ".$ar_total.PHP_EOL; // array_reduce:total = 17

foreachの処理と比べるとあまり直感的ではないような気がします。

array_sumとarray_columnを組み合わせる

array_columnで特定のキーのみを抽出した配列を取得します。その配列の合計値をarray_sumで求めます。
array_sum
array_column

$as_total = array_sum(array_column($arrData, 'quantity'));
echo "array_sum:total = ".$as_total.PHP_EOL; // array_sum:total = 17

結局どれがいいの?

結果はどれも一緒なので好きなの使ってもいいんですが、順位付けのためそれぞれの処理時間を計測してみます。

// 100万個のデータを作成
$arrData = array();
for ($i = 0; $i < 1000000; $i++) {
    array_push($arrData, array('name' => 'hoge', 'quantity' => rand(0, 10)));
}

$start = microtime(true);
foreach ($arrData as $data) {
    $f_total += $data['quantity'];
}
$end = microtime(true);
$elapsed = $end - $start;
echo "foreach:time = ". $elapsed ."[sec]".PHP_EOL; // foreach:time = 0.14497804641724[sec]

$start = microtime(true);
$ar_total = array_reduce($arrData, function($carry, $item){
            return $carry += $item['quantity'];
        });
$end = microtime(true);
$elapsed = $end - $start;
echo "array_reduce:time = ". $elapsed ."[sec]".PHP_EOL; // array_reduce:time = 0.16593194007874[sec]

$start = microtime(true);
$as_total = array_sum(array_column($arrData, 'quantity'));
$end = microtime(true);
$elapsed = $end - $start;
echo "array_sum:time = ". $elapsed ."[sec]".PHP_EOL; // array_sum:time = 0.076914072036743[sec]
方法 処理速度[sec]
foreachでループする 0.14497804641724
array_reduceとクロージャを組み合わせる 0.16593194007874
array_sumとarray_columnを組み合わせる 0.076914072036743

処理速度を比べてみるとarray_sumとarray_columnを組み合わせた方法が明らかにベストですね。