PHPで配列から空白を取り除きたい

今までもちょいちょい使う事があったけど、WEB アプリだと気持ち使用頻度が高い気がするので書いておく。

<?php
$hoge = ["ehehe", "ohoho", null, "piyo", null];
?>
~(中略)~
<?php foreach($hoge as $row): ?>
<?php if($row): ?>
<li><?php echo $row; ?>中身入ってるならなんか処理する</li>
<?php endif; ?>
<?php endforeach; ?>

雑な例えだが、HTML にリストを出力する際に、上記のようなコードの $hoge には「そもそも空白は入っていない」と言う前提があれば、Line:6 のような分岐が必要なくなるよねって感じ。

 

array_filter を使う

コード的に一番短いので、管理人はよく使っている。

サンプルコード

<?php
$data = ["ehehe", "ohoho", null, "piyo", null];
$hoge = array_filter($data);
?>
~(中略)~
<?php foreach($hoge as $row): ?>
<li><?php echo $row; ?>中身は入ってる前提から全部処理する</li>
<?php endforeach; ?>

よく使ってはいるけど、ちゃんと「何故そうなるのか」は理解しておいた方が良い。

array_filter 自体は便利な関数なので「配列から空白を除去する関数」と言う覚え方をしないようにするのが大事。

array_filter の仕様

array_filter(array $array, ?callable $callback = null, int $mode = 0): array

そもそも array_filter は、第2引数に渡したコールバック関数によって要素をフィルタリングする関数。

第3引数はコールバックへ渡す内容を決めるフラグで、ここでは深く触れないが、デフォルトは「値だけを渡す」となっている。(※他はキーだけ渡すとかどっちも渡すとか。)

で、何故これが空白を除去するのかについては、第2引数の挙動を見れば分かる。

コールバック関数が与えられなかった場合、 array のエントリの中で空のものはすべて削除されます。 この場合の「空」の定義については、empty() 関数を参照ください。

引用 – php.net

これですね、PHP の empty 関数で true が返って来る値は全て削除されるため、結果的に「空白を除去する」と言う挙動になるって事だね。

つまり「null」は消して欲しいけど、空白文字列「""」は残して欲しいと言うケースでは、第2引数のコールバックを略せずに書かなければ行けないので注意されたし。

 

array_diff を使う?

ネットで検索してみたら array_diff を使うパターンも紹介されていたのでちょっと試してみたら、PHP 特有の「型を自動的にキャスト(変換)」する仕様が分かりにくい感じになっていた。

これはこう言う使い方をした事が無いので、勉強がてら書いておく。

検証コード

<?php
$data = ["ehehe", "ohoho", null, "piyo", null];
$diff1 = array_diff($data, [0, "", null, false]);
$diff2 = array_diff($data, [0]);
$diff3 = array_diff($data, [""]);
$diff4 = array_diff($data, [null]);
$diff5 = array_diff($data, [false]);

print_r($diff1);  // Array ( [0] => ehehe [1] => ohoho [3] => piyo )
print_r($diff2);  // Array ( [0] => ehehe [1] => ohoho [2] => [3] => piyo [4] => )
print_r($diff3);  // Array ( [0] => ehehe [1] => ohoho [3] => piyo )
print_r($diff4);  // Array ( [0] => ehehe [1] => ohoho [3] => piyo )
print_r($diff5);  // Array ( [0] => ehehe [1] => ohoho [3] => piyo )

$diff1 と $diff2 は分かる、と言うより期待していた挙動だが $diff3 ~ $diff5 は期待していた結果とは異なるものになっているのが分かる。

array_diff の仕様

array_diff(array $array, array ...$arrays): array

第1引数の配列を他(=第2引数以降)の配列と比較して、他の配列に含まれていない要素のみを含む配列を返すのが array_diff の仕様とある。

empty 関数で true が返って来るものを幾つも指定した $diff1 はともかく、$diff2 では「0」の場合は除外となるため、この挙動は納得だ。

しかし $diff3 以降は全て $diff1 と同じ挙動になっているのは明らかに気持ち悪い、何故なら同じ結果になって欲しかったのは $diff4 だけだからだ。

その原因がこの仕様。

例2 型が一致しない場合の array_diff() の例

(string) $elem1 === (string) $elem2 の場合のみ、 つまり、文字列表現 が同等な場合のみ、 2つの要素は等しいとみなされます。

引用 – php.net

比較する型が違う場合、どちらも「文字列に変換されてから比較」されてしまっているからだ。

つまりこう言う事。

echo (string) 0;  // "0"
echo (string) "";  // 
echo (string) null;  // 
echo (string) false;  // 

空白文字列も null も false も文字列に変換すると、全部空白文字列になってしまうため、一致条件を満たしてしまう事が分かる。

int 型の 0 は文字列の “0" になるので回避出来たが、型が違う場合は変換されてしまうので、比較元に「"0″」が存在する場合は int 型の 0 と一致条件を満たしてしまうなどの問題も見て取れるため「空白を除外する」と言う用途で使うなら、やはり注意が必要だろう。

 

まとめ

実際の所、array_filter と言うより、PHP を使っている以上は型の自動変換はあらゆる動作で発生しているため、先述の通り「配列から空白を除去する関数」と言う覚え方をしないようにするのが大事、と言うのを忘れずに使って欲しいと思う。

array_filter も array_diff も、適した処理で使う分にはイイヤツだし。

PHParray_diff,array_filter

Posted by theuri