かもメモ

自分の落ちた落とし穴に何度も落ちる人のメモ帳

PHP 配列のキーとインデックスについてのメモ

PHPksortarray_filter を使っていて整数キーを与えた配列をループさせた際のインデックスで少し戸惑ったのでメモ

環境

PHP v8.2.0
tehplayground.com 環境でテスト

PHP の配列のキーとインデックスは同じ整数でも別物

配列 key は、整数 または 文字列です。 value には任意の型を指定できます。
PHP においては添字配列と連想配列の間に違いはなく、配列型は 1 つだけで、 同じ配列で整数のインデックスと文字列のインデックスを同時に使えます。
cf. PHP: 配列 - Manual

意図的に整数のキーを与えてもインデックスとは別にキーが保持されている

配列をループで回す際はあくまで インデックス の順番に取り出される

<?php
function key_index(array $arr) {
  $index = 0;
  foreach($arr as $key => $item) {
    echo "{$index} - {$key}: {$item}\n";
    $index+=1;
  }
}

key_index(['Ichigo', 'Aoi', 'Ran']);
// 0 - 0: Ichigo
// 1 - 1: Aoi
// 2 - 2: Ran

key_index([0 => 'Ichigo', 1 => 'Aoi', 3 => 'Ran']);
// 0 - 0: Ichigo
// 1 - 1: Aoi
// 2 - 2: Ran

// キーはあくまでキー
key_index([1 => 'Akari', 3 => 'Sumire', 5 => 'Hinaki']);
// 0 - 1: Akari
// 1 - 3: Sumire
// 2 - 5: Hinaki

// キーとインデックスは別物なのでキーが整数でもインデックス順になっている
key_index([5 => 'Shion', 3 => 'Otome', 4 => 'Sakura']);
// 0 - 5: Shion
// 1 - 3: Otome
// 2 - 4: Sakura

array_search で取得できるのはキー

<?php
$arr = [1 => 'Akari', 3 => 'Sumire', 5 => 'Hinaki'];
array_search('Akari', $arr, true);
// => 1 

array-slice, array-splice の位置指定はインデックス

array-slice
<?php
$arr = [
  1 => 'Akari',
  0 => 'Ichigo',
  5 => 'Hinaki',
  4 => 'Ran',
  2 => 'Aoi',
  3 => 'Sumire'
];

array_slice($arr, 1, 3);
// array(3) {
//   [0]=> "Ichigo"
//   [1]=> "Hinaki"
//   [2]=> "Ran"
// }

// key を維持する場合は第三引数を true にする
array_slice($arr, 1, 3, true);
// array(3) {
//   [0]=> "Ichigo"
//   [5]=> "Hinaki"
//   [4]=> "Ran"
// }
array-splice

array_splice はキーが整数の場合は振り直される

<?php
$arr = [3 => 'Akari', 2 => 'Sumire', 1 => 'Hinaki'];
array_splice($arr, 1, 0, 'Mizuki');
var_dump($arr);
// array(4) {
//   [0]=> "Akari"
//   [1]=> "Mizuki"
//   [2]=> "Sumire"
//   [3]=> "Hinaki"
// }

$arr = ['a' => 'Ichigo', 'b' => 'Aoi', 'c' => 'Ran'];
array_splice($arr, 1, 0, 'Mizuki');
var_dump($arr);
// array(4) {
//   ["a"]=> "Ichigo"
//   [0]=> "Mizuki"
//   ["b"]=> "Aoi"
//   ["c"]=> "Ran"
// }

array_filter は配列の要素をインデックスの順番に取り出して捜査し新しい配列を返すが元の配列のキーが維持される

新しい配列を返すのでキーが数値の場合インデックスに振り直されそうなイメージだったが元のキーが維持される

<?php
$arr = [
  1 => 'Akari',
  0 => 'Ichigo',
  5 => 'Hinaki',
  4 => 'Ran',
  2 => 'Aoi',
  3 => 'Sumire'
];

$newArray = array_map(fn($item) => "{$item}♡", $arr);
var_dump($newArray);
// array(6) {
//   [1]=> "Akari♡"
//   [0]=> "Ichigo♡"
//   [5]=> "Hinaki♡"
//   [4]=> "Ran♡"
//   [2]=> "Aoi♡"
//   [3]=> "Sumire♡"
// }

$soleil = ['Ichigo', 'Aoi', 'Ran'];
$newArray = array_filter($arr, fn($item) => in_array($item, $soleil, true));
var_dump($newArray);
// array(3) {
//   [0]=> "Ichigo"
//   [4]=> "Ran"
//   [2]=> "Aoi"
// }

所感

PHP で配列を扱う際に、この位置に要素を入れたいと思って添字に整数を使って $array[n] = $item としても配列のインデックスは別なのでループさせた際に添え字の順番で取得されない。意図的な添字の順にしたい場合は ksort で意図的に並び替える必要がある。

キーにインデックスと同じ整数値を使えるので言語仕様を覚えておかないと意図しないバグを出してしまいそうだな〜と思いました。(JavaScript がゆるふわなだけ説あり…)
そういう意味でユニットテスト大切にしたい…です!!


[参考]