hnwの日記

array_unique関数の重複の意味に注意

僕は滅多に使わないのですが、PHPにはarray_uniqueという関数があります。

array_unique ― 配列から重複した値を削除する


説明
array array_unique ( array $array [, int $sort_flags ] )


array を入力とし、値に重複のない新規配列を返します。


PHP: array_unique - Manual


ここまでは良いのですが、続いてこんな注意書きも見つかります。

注意: (string) $elem1 === (string) $elem2 の場合のみ二つの要素は等しいとみなされます。 言い換えると、文字列表現が同じ場合となります。 最初の要素が使用されます。


PHP: array_unique - Manual


つまり、array_unique関数の「重複」というのは、文字列型にキャストした値同士が等しいという意味です。単なる===ではありません。array_intersect関数(配列の共通項を計算する)やarray_diff関数(配列の差を計算する)などでも同様です。


文字列へキャストしてから比較するということは、strcmp関数と同様に注意が必要だということです(参考:「PHPで==の代わりにstrcmp関数を使うことによる問題点」)。例を示します。

$ php -r 'var_dump(array_unique(array(true,1,1.0,"1")));'
array(1) {
  [0]=>
  bool(true)
}


整数の1や文字列の"1"はtrueと重複している扱いになります。

$ php-5.2.1 -r 'print_r(array_unique(array(0.0,-1/1e1000,1200000,1200000.0)));'
Array
(
    [0] => 0
    [2] => 1200000
)
$ php-5.2.2 -r 'print_r(array_unique(array(0.0,-1/1e1000,1200000,1200000.0)));'
Array
(
    [0] => 0
    [2] => 1200000
    [3] => 1.2E+6
)
$ php-5.2.3 -r 'print_r(array_unique(array(0.0,-1/1e1000,1200000,1200000.0)));'
Array
(
    [0] => 0
    [1] => -0
    [2] => 1200000
    [3] => 1.2E+6
)
$ php-5.2.8 -r 'print_r(array_unique(array(0.0,-1/1e1000,1200000,1200000.0)));'
Array
(
    [0] => 0
    [1] => -0
    [2] => 1200000
)


このように、浮動小数点数を含んだ配列をarray_uniqueするとバージョンごとに結果が変わる例があります。どうしてもarray_unique関数を使う場合には文字列型か整数型しか含まないという保証が必要でしょう。


個人的にはarray_系の関数はできるだけ避けるべきだと考えています。というのも、本当は連想配列のキーとして扱う方が向いているデータを配列で扱っている例が多い気がするからです。array_uniqueなどは特にそんな気がします。