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年の終わりに、配列関数を勉強しなおしてみてはどうでしょうか。