2026.09.23 泊まれる場所4,223 立ち寄りスポット59,693 営業中を確認したキャンプ場1,005 note を読む

PHPのarray_filterでキーが飛ぶ理由|結果をjson_encodeすると、配列ではなくオブジェクトになる

array_filter()をかけた配列をjson_encode()すると、[2,4,6]ではなく{"1":2,"3":4,"5":6}が返ってくる。
キーが残る性質と、array_values()での直し方を、PHP 8.3で動かした出力つきで整理した。

PHPで array_filter() をかけた配列を、そのまま json_encode() に渡す。
すると、[2,4,6] のような配列ではなく、{"1":2,"3":4,"5":6} というオブジェクトが返ってくることがある。
原因は array_filter() が、元のキーを残したまま要素を取り除くことにある。

この記事は、PHPマニュアルを読み、手元の PHP 8.3 で実際に動かした出力を並べて、僕が理解した範囲で整理したものだ。
自分のサイトで起きた不具合の話ではないので、体験談は入れていない。
仕様と書き方の話に絞った。

偶数だけ残したら、キーが 1, 3, 5 になる

1から6までの配列から、偶数だけを残してみる。

$a = [1, 2, 3, 4, 5, 6];
$even = array_filter($a, fn($n) => $n % 2 === 0);
print_r($even);

PHP 8.3 で実行すると、出力は次のようになった。

Array
(
[1] => 2
[3] => 4
[5] => 6
)

値は 2, 4, 6 の三つだけが残っている。
ただし、キーは 0, 1, 2 に詰め直されず、元の位置だった 1, 3, 5 のままだ。
PHPマニュアルにも「配列のキーは保存される」と書かれている。

連番に見えて、連番ではない

キーが 1, 3, 5 の配列は、for 文で $even[0] から順に読むと、$even[0] が存在しない。
foreach で回す分には問題にならないので、気づかないまま使えてしまう。
マニュアルは、元が連番の配列だとキーに歯抜けが生じることがある、と注意している。

困りやすいのは、この配列を JSON にするときだ。
PHPマニュアルの json_encode のページには、配列のキーが 0 から始まる連続した数値でなければ、オブジェクトとしてエンコードされる、とある。

echo json_encode($even);
// {"1":2,"3":4,"5":6}

JavaScript 側で配列だと思って .map() を呼ぶと、オブジェクトには map がないのでエラーになる。
PHP の画面では print_r で確認できても、JSON になった瞬間に形が変わる、という点が落とし穴になる。

array_values() でキーを振り直す

マニュアルが示している解決策は、array_values() で結果のキーを振り直すことだ。

echo json_encode(array_values($even));
// [2,4,6]

array_values() は値だけを取り出して、0 から順にキーを付け直した配列を返す。
こちらも手元の PHP 8.3 で、[2,4,6] になることを確かめた。

キーに意味がある連想配列(例えば ID をキーにした配列)なら、振り直してはいけない。
キーが番号でしかないときだけ、array_values() を通す、と覚えている。

コールバックを省略すると、空の値が消える

array_filter() には、コールバックを渡さない書き方もある。
マニュアルでは、その場合 array のエントリのうち「空」のものはすべて削除される、と説明されている。
「空」の定義は empty() 関数のページに書かれている。

$z = ["a", "", null, 0, "0", false, "b"];
print_r(array_filter($z));

この出力は、次のとおりだった。

Array
(
[0] => a
[6] => b
)

空文字、null、0、文字列の "0"、false がまとめて消えて、"a" と "b" だけが残った。
ここでもキーは 0 と 6 のままだ。
数字の 0 や "0" を残したい場面では、コールバックを省略せず、どの値を残すか自分で書いたほうが安全だと僕は思う。

キーで絞りたいときのモード指定

第3引数に定数を渡すと、コールバックに渡されるものを変えられる。
ARRAY_FILTER_USE_KEY を指定すると、値ではなくキーだけがコールバックに渡る。
ARRAY_FILTER_USE_BOTH なら、値とキーの両方が渡る。

どちらのモードでも、結果のキーが保存されるという性質は変わらない。
連想配列から特定のキーだけを取り出す場面でも、元のキーがそのまま残る。

僕の使い分け

array_filter() の結果を foreach で回すだけなら、そのままで構わない。
JSON にする、for 文で添字を使う、といった「0 から連番であること」を前提にする場面では、array_values() を一枚かませる。
この二択で、僕は困らないと考えている。

JSON まわりでは、日本語がエスケープされる件をjson_encodeの記事にまとめた。

出典

まる子パパ

まる子パパ

会社員。受託開発のエンジニアを15年以上続けていて、その前は寿司職人・長距離運転手・農業。 技術ブログは、キャンプの合間に踏んだバグと、AI・Web開発の運用の失敗を書いています。 このサイト自体が、開発に携わっているCMS「コンテナ」の稼働中の実例です。 このサイトについて / 小さな自動化の相談

—

ほかにも書いています

受託・note・スタンプ

※受託の相談とnoteは、運営者が個人で受けている・書いているものです。

技術ブログ一覧へ戻る