new takyam();

Qiitaぽい話はQiitaに書いていくことにする気がする http://qiita.com/takyam

PHPのarray系関数ちゃんと使ってる?foreachとの比較

PHPで配列の処理をする時、foeachをよく使います。これは私だけではなく、恐らく多くのPHPerがそうだと思います。

しかしながら、PHPには多くのarray_xxxx系標準関数(以下:array系関数)が用意されており、標準関数で行える事は標準関数で処理したほうが、スマートで、高速です。

初級編

初級編では、超多様するin_array()などの基礎的なものはスキップして、たまに使うレベルのものをいくつかご紹介したいと思います。

array_sum()

まずは、配列の中の数値の合計を返してくれるarray_sum関数です。

array_sum()
http://php.net/manual/ja/function.array-sum.php

<?php
$ary = array(100, 200, 300);
//この配列の合計が欲しい場合

//foreachの場合
$sum = 0;
foreach($ary as $num){
    $sum += $num;
}

echo $sum; //> 600

//array_sumの場合
$sum = array_sum($ary);

echo $sum; //> 600

foreachで4行必要な処理が1行で書けました!

array_unique()

次に、foreachだと若干面倒くさい、array_unique関数をご紹介します。

array_unique()
http://php.net/manual/ja/function.array-unique.php

この関数は、配列の中から重複を取り除いてユニークな状態の配列を返す非破壊関数です。
非破壊なので、必ず返り値を受け取ってください。

<?php
//この配列を array('a', 'b', 'c') にしたい場合
$ary = array('a', 'b', 'a', 'c');

//foreachで行う場合
$uniq_ary = array();
foreach($ary as $str){
    if(!in_array($str, $uniq_ary)){
        $uniq_ary[] = $str;
    }
}

var_export($uniq_ary);
/*
array (
  0 => 'a',
  1 => 'b',
  2 => 'c',
)
*/

//array_uniq()を使う場合
$uniq_ary = array_unique($ary);

var_export($uniq_ary);
/*
array (
  0 => 'a',
  1 => 'b',
  3 => 'c',
)
*/

array_unique関数は、元の配列のキーのうち、最初に出てきたものをそのまま利用しますので、foreachとキーが異なってはいますが、値の方は全く同じになっています。

これもまたforeachと違ってすごくスマートですね。

array_merge()

array_merge関数は、本来2つの配列をマージする時に使う関数です。 array() + array() と似ていますが、 重複キーにおいて優先される値が異なりますので、違いを理解したうえで使ってください。

array_mege()
http://php.net/manual/ja/function.array-merge.php

今回はその説明は省き、最近自分がよく使う用途についてご紹介したいと思います。

先ほどご紹介した、array_unique関数では、
foreachの場合と違い、キーが元の配列のままになっていました。

しかし、配列のキーを0から振り直したい場合などに、
array_merge関数を使う事ができます。

<?php
$uniq_ary = array_merge(array_unique($ary));
var_export($uniq_ary);
/*
array (
  0 => 'a',
  1 => 'b',
  2 => 'c',
)
*/

こんな感じで、array_merge()関数に渡す事で、0からキーを振りなおしてくれます。
マージする段階でキーを0から振り直すのと、第2引数以後はoptionalなので出来る、便利なTipsです。

初級編まとめ

こんな感じで、関数に配列を渡すだけでも、いろんな処理を行なってくれる配列があります。

当然ここでご紹介したのはほんのごく一部なので、公式ドキュメントをご覧になる事をオススメします。

上級編

初級編では、割りと決まった処理を行う関数をご紹介しましたが、上級編では、処理の内容を自分で書く関数をいくつかご紹介します。

これらのarray系関数の多くで、functionを引数として渡します。5.3以降では、クロージャが使えますので、create_functionなんて前時代的な事はやめて、クロージャを清く正しく美しく使いましょう。

array_map()

array_map()は、配列の各値に特定の処理を行う時に便利な関数です。
これも非破壊関数なので、返り値を必ず受け取るようにしてください。

array_map()
http://php.net/manual/ja/function.array-map.php

<?php
//この配列の中の値をそれぞれ2倍にしたい場合
$ary = array(1, 2, 3, 4, 5);

//foreachの場合
$doubled_ary = array();
foreach($ary as $num){
    $doubled_ary[] = $num * 2;
}
var_export($doubled_ary);
/*
array (
  0 => 2,
  1 => 4,
  2 => 6,
  3 => 8,
  4 => 10,
)
*/

//array_mapの場合
$doubled_ary = array_map(function($num){
    return $num * 2;
}, $ary);
var_export($doubled_ary);
/*
array (
  0 => 2,
  1 => 4,
  2 => 6,
  3 => 8,
  4 => 10,
)
*/

foreachとarray_mapとで、特に処理の仕方が代わったようにも見えませんし、結果も一緒です。
が、たとえばこの処理を1万回ループさせると、約5倍ほどforeachの方が早いですw

とはいえ、通常のアプリケーション内では、どちらで書いても、誤差もない程度の差しかありませんので、配列に対して何らかの処理を行う場合の、シャレオツな書き方として覚えておくと良いとおもいます。

array_walk()

array_mapに似た関数に、array_walk関数があります。

array_walk()
http://php.net/manual/ja/function.array-walk.php

array_mapとの違いは、array_walkが、破壊的メソッドである点です。

<?php
//配列の中の値をそれぞれ2倍にしたい場合
//foreachの場合
$ary = array(1, 2, 3, 4, 5);
foreach($ary as $key => $num){
    $ary[$key] = $num * 2;
}
var_export($ary);
/*
array (
  0 => 2,
  1 => 4,
  2 => 6,
  3 => 8,
  4 => 10,
)
*/

//array_mapの場合
$ary = array(1, 2, 3, 4, 5);
array_walk($ary, function(&$num, $key){
  $num = $num * 2;
}, $ary);
var_export($ary);
/*
array (
  0 => 2,
  1 => 4,
  2 => 6,
  3 => 8,
  4 => 10,
)
*/

破壊的メソッドといいつつ、クロージャの引数をリファレンスで受け取る必要があります。
array_walkの返り値は、true/false なので、間違わないようにしてください。

そして、これもarray_mapと同じように、foreachのほうが早かったりします。
いったい何のための関数なんでしょうね。

array_walk_recursive()

最後にarray_walk_recursive関数をご紹介します。

array_walk_recursive()
http://php.net/manual/ja/function.array-walk-recursive.php

再帰的なarray_walk関数という名前どおり、多次元配列の場合もすべての要素に対して、ユーザー関数を実行してくれます。

<?php
//配列の中の値をそれぞれ2倍にしたい場合
//foreachの場合
$ary = array(1, 2, 3, 4, 5, array(6, 7));
function double($ary)
{
    foreach ($ary as $key => $num) {
        if(is_array($num)){
            $value = double($num);
        }else{
            $value = $num * 2;
        }
        $ary[$key] = $value;
    }
    return $ary;
}
$ary = double($ary);
var_export($ary);
/*
array (
  0 => 2,
  1 => 4,
  2 => 6,
  3 => 8,
  4 => 10,
  5 =>
  array (
    0 => 12,
    1 => 14,
  ),
)
*/

//array_mapの場合
$ary = array(1, 2, 3, 4, 5, array(6, 7));
array_walk_recursive($ary, function (&$num, $key) {
    $num = $num * 2;
}, $ary);
var_export($ary);
/*
array (
  0 => 2,
  1 => 4,
  2 => 6,
  3 => 8,
  4 => 10,
  5 => 
  array (
    0 => 12,
    1 => 14,
  ),
)
*/

再帰的な処理を行う場合、foreachだと、どうしてもfunctionを作って再帰的にしなければなりませんが、array_walk_recursiveだと非常にスマートに書く事ができます。

array_walk関数の存在意義はよくわかりませんが、array_walk_recursive関数のための布石と考えると納得です。

速度的にも、foreach+functionの場合より高速になっています。

まとめ

PHP公式ドキュメントの配列関数のページを見ると、約80の関数が紹介されています。

配列関数
http://php.net/manual/ja/ref.array.php

配列に対して行う処理の多くは、foreachで自前実装する事ができると思います。
しかしながら、標準で用意されている多くの関数を使いこなす事で、実装する時間を削減し、バグの発生を防ぎ、もしかすると高速な処理を実現する事ができるかもしれません。

使わないなら使わないで何とかなっちゃったりしますが、
2012年の終わりに、配列関数を勉強しなおしてみてはどうでしょうか。