配列処理の使い方-for?foreach?それともwhile?-

こんにちわ、エンジニア婦人です。主にPHPを扱うwebエンジニアです。

突然ですが、

「for / foreach / whileのどれが一番スマートな書き方になる?」

クイズです。

以下の事例では、処理自体はどれでもできる場合があります。
「どれでもできるだろうけど、これが一番スマートだよ」というものが正解です。

for or foreach or while?クイズ

第1問

0~100までのランダム生成される整数が格納された配列を加算し、偶数になるまで処理を続行したい。for / foreach / whileのいずれを使うと最も適切か。

第2問

以下の配列データ10個を分解し、それぞれのデータの精査をしたい(名前の文字数、ユーザーIDの半角英数字確認など)。for / foreach / whileのいずれを使うと最も適切か。

処理したいデータ
$data[0]['name'] = "村山さん";
$data[0]['user_id'] = "system_user_01";
$data[0]['code'] = "system_user_01_code";

$data[1]['name'] = "里村さん";
$data[1]['user_id'] = "system_user_02";
$data[1]['code'] = "system_user_02_code";

$data[2]['name'] = "田村さん";
$data[2]['user_id'] = "system_user_03";
$data[2]['code'] = "system_user_03_code";

$data[3]['name'] = "鳩村さん";
$data[3]['user_id'] = "system_user_04";
$data[3]['code'] = "system_user_04_code";

$data[4]['name'] = "村谷さん";
$data[4]['user_id'] = "system_user_05";
$data[4]['code'] = "system_user_05_code";

第3問

配列30個分のランダム数字のデータを格納したあと、出力したい。for / foreach / whileのいずれを使うと最も適切か。

基本の使い方

問題文に「for / foreach /whileのいずれを使うと最も適切か。」と書きました。今回は「for / foreach /while」の使い分けのお話です。

さて、PHPでは配列処理がつきものです。

実際のところ、for、foreach、whileのどれを使っても配列処理はできます。ですが、それぞれの長所を生かした書き方をすると直感的にわかりやすいものを使った方があとで見返したときにわかりやすくなる(可読性が良い)、その結果バグが混入しにくくなると思います。

それでは「for / foreach / while」のそれぞれの使い方を見ていきます。

for文

以下のような使い方になります。

for構文
for($i=0; $i<10; $i++) {
    echo $i . "回目のループ";
}

for文の得意なこと

for文は「繰り返し回数が定まっているとき」が得意です。

例えばデータベースから取得する項目が「col_1」~「col_10」まであるような場合や、ランダム数値を10個出す処理(ランダム文字列パスワード生成など)、空の配列を先に宣言しておきたいときに使われます。

col_1~10のデータを取得したい
// col_1~10の入ったデータを$dataとしています
for($i=1; $i<=10; $i++) {
    if(empty($data['col_' . $i])) continue;
    echo 'col_' . $i . 'の中身:' . $data['col_' . $i];
}

for文の苦手なこと

回数が一定に決まらない処理が苦手です。$data配列の数が10個なのか、20個なのか、はたまた100個なのかわからないときには向いていません。

また、内部の書き方によっては無限ループが発生する場合があります。
以下は無限ループが発生する可能性がある例です。

無限ループの例
for($i=1; $i<=10; $i++) {
    // 特定の条件でfor文のカウント回数をリセットする
    if(empty($data[$i]) && $i%2===0) {
        $i=0;
        echo "リセット";
    }
    echo $i . "回目のループ";
}

この場合、$iが10になる前に$data[$i]が空になり、且つ$iが偶数の場合は$iがリセットされます。一度この条件を満たすようなデータが入るとループを抜けられなくなります。

foreach文

以下のような使い方になります。

foreach構文
// $dataが連想配列であると仮定
foreach($data as $d) {
    echo "名前:" . $d['name'];
    echo "住所:" . $d['address'];
    echo "備考:" . $d['note'];
}

foreach文の得意なこと

foreach文は「連想配列の処理が得意(絶対的な強さを誇る)」です。

PHPではデータベースからデータを取得してきたときでも、フォームからPOSTされてくるときでも、とにかく配列を目にする機会がとても多いと思います。

初めはとっつきにくいものですが、なんとかかんとかある日突然わかるようになります(著者がそうでした。)
foreach文を自在に操ることができると、書き方の幅が広がります。

foreach文の苦手なこと

指定回数を繰り返す必要があるときや、空の配列を指定数作りたいとき、そもそものデータが配列ではないときは使えません。

また、処理を開始するときに分解したいデータが配列でない場合はNoticeが発生します。

foreach構文
// $dataが配列でないときはNoticeが発生するので、防ぐために条件文を追加すると安全
if(is_array($data)) {
    foreach($data as $d) { 
        echo "名前:" . $d['name'];
        echo "住所:" . $d['address'];
        echo "備考:" . $d['note']; // $d['note']が存在しない場合もNotice
    }
}

while文

以下のような使い方になります。

while構文
$cnt = 1;
$i=0;
while($i !== 10) {
    echo $cnt . "回目のループ";
    if($cnt%2 === 0) $i++;
    $cnt++;
}

while文の得意なこと

while文は「指定の条件を満たすまでのループ処理」が得意です。

指定の条件を満たすまでは続けたい処理があるときに向いています。

while文の苦手なこと

条件を満たすまで実行し続けるので、無限ループ事故を引き起こしやすいのが難点です。

補足:do – while文

do – while文は「何があろうとも一回は必ず処理を行いたい」、「その後、条件によってはループして処理したい」ときに使います。

例えば、

「前の処理の結果である変数がtrueのときはループ処理に入るが、falseのときは入らない場合」は「while文」

「前の処理の結果がどうであれ、必ず一回は処理するが、それ以降は前の処理の結果である変数がtrueのときのみ実行する場合」は「do-while文」

となります。

do-while構文
// $resultがfalseの場合は$iが10になるまでループします
// $resultがfalseの場合でも、1回は通過します。
$result = true;
$i = 0;
do {
    echo "do-while文";
    if($i === 10) $result = false;
    $i++;
} while($result);

クイズの解答例

最後に、冒頭のクイズの解答例を掲載します。

色々な方法がありますが、私ならこうやって解決します。

第1問

0~100までのランダム生成される整数が格納された配列を加算し、偶数になるまで処理を続行したい。for / foreach / whileのいずれを使うと最も適切か。

正解:while

正解例
$data = [];
$data[] = mt_rand(0, 100);
$data[] = mt_rand(0, 100);
while(($data[0] + $data[1]) % 2 !== 0) {
    $data = [];
    $data[] = mt_rand(0, 100);
    $data[] = mt_rand(0, 100);
}
// またはdo-while
do {
    $data = [];
    $data[] = mt_rand(0, 100);
    $data[] = mt_rand(0, 100);
} while(($data[0] + $data[1]) % 2 !== 0);
echo $data[0];
echo $data[1];
echo $data[0] + $data[1];

実行サンプルはこちらから確認できます(エンジニア婦人ノートデモページへ飛びます)。

第2問

以下の配列データ10個を分解し、それぞれのデータの精査をしたい(名前の文字数、ユーザーIDの半角英数字確認など)。for / foreach / whileのいずれを使うと最も適切か。

正解:foreach

正解例
if(is_array($data)) {
    foreach($data as $key=>$d) {
        if(isset($d['name'])) {
            // nameのときにやりたい処理
        }
        if(isset($d['user_id'])) {
            // user_idのときにやりたい処理
        }
        if(isset($d['code'])) {
            // codeのときにやりたい処理
        }
    }
}

実行サンプルはこちらから確認できます(エンジニア婦人ノートデモページへ飛びます)。

第3問

配列30個分のランダム数字のデータを格納したあと、出力したい。for / foreach / whileのいずれを使うと最も適切か。

正解:for、foreach文(表示はfor / whileでも可能)

正解例
$data = [];
for($i=0; $i<30; $i++) {
    $data[] = mt_rand(0,100);
}
foreach($data as $d) {
    echo $d;
}

// forの場合 30個と決まっているので可能
for($i=0; $i<30; $i++) {
    echo $data[$i];
}

// whileの場合
$i = 0;
while(isset($data[$i])) {
    echo $data[$i];
    $i++;
}

実行サンプルはこちらから確認できます(エンジニア婦人ノートデモページへ飛びます)。

まとめ

  • for / foreach / while はどれも適材適所
  • for文は「繰り返し回数が定まっているとき」が得意で、「回数が定まらない処理」が苦手
  • foreach文は「連想配列の処理が得意(絶対的な強さを誇る)」で、「指定回数を繰り返す必要があるときや、空の配列を指定数作りたいとき、そもそものデータが配列ではないとき」が苦手
  • while文は「指定の条件を満たすまでのループ処理」が得意で、「条件を満たすまで実行し続けるので、無限ループ事故を引き起こしやすい」のが難点

※実行サンプルソースはGitHubで公開しています。

理解の助けになれば幸いです。参考にしてみてください(‘ω’)ノ