【PHP】配列のキーを維持したまま array_column を使いたい

##array_column について
PHPの[`array_column`](https://www.php.net/manual/ja/function.array-column.php)は，多次元配列から単一のカラムの値を返してくれる，[とても便利な関数](https://qiita.com/harukasan/items/a0773aef27d838852e44)です。

```php:php
// このような多次元配列から…
$array = [
  2 => [ 'id' => 111, 'name' => 'suzuki', 'age' => 21 ],
  3 => [ 'id' => 222, 'name' => 'sato',   'age' => 34 ],
  5 => [ 'id' => 333, 'name' => 'tanaka', 'age' => 55 ],
];

// 単一のカラム値の配列を作れる
var_export( array_column( $array, 'name' ) );
// array (
//   0 => 'suzuki',
//   1 => 'sato',
//   2 => 'tanaka',
// )
```

しかしながら，返り値の配列のキーは

- **第3引数を指定しない場合**：単純に0から振り直される
- **第3引数を指定した場合**　：指定カラムの値がキーとして使われる

のどちらかで，元の配列のキー（上の例だと「2」「3」「5」）は維持されません。

##元の配列のキーを維持したいとき

次の3通りの方法を紹介します。

- **方法①** `array_column`と他の関数を組み合わせる
- **方法②** `array_map`を使う
- **方法③** `foreach`を使う

※2020/10/14　当記事の初投稿時には**方法①**のみを紹介していたのですが，コメント欄で@tadsanさんに教えていただいた**方法②**に加えて，foreachを使う場合の**方法③**も追加しました。また，まとめの内容を変更し，`array_column`についての蛇足も追記しました。

<br>
###方法①`array_column`と他の関数を組み合わせる

[`array_keys`](https://www.php.net/manual/ja/function.array-keys.php)で元の配列のキーを取得し，第3引数を指定しない`array_column`の返り値と合体させましょう。対応する配列の生成には[`array_combine`](https://www.php.net/manual/ja/function.array-combine.php)を使います。

```php:php
$array = [
  2 => [ 'id' => 111, 'name' => 'suzuki', 'age' => 21 ],
  3 => [ 'id' => 222, 'name' => 'sato',   'age' => 34 ],
  5 => [ 'id' => 333, 'name' => 'tanaka', 'age' => 55 ],
];

// キーを配列として取得
$keys = array_keys( $array ); // => [ 2, 3, 5 ]
// 指定要素を配列として取得
$values = array_column( $array, 'name' ); // => [ 'suzuki', 'sato', 'tanaka' ]
// それぞれの配列を合体
$combined = array_combine( $keys, $values );
// 出力
var_export( $combined );
// array (
//   2 => 'suzuki',
//   3 => 'sato',
//   5 => 'tanaka',
// )
```

上記の操作を1行で書くと以下のようになります。

```php:php
array_combine( array_keys( $array ), array_column( $array, 'name' ) );
```

- **メリット**　：関数の使い方として何をやっているかは分かりやすい
- **デメリット**：関数の呼び出しが多い

<br>
###方法②`array_map`を使う
「**`array_column`を使いたい**」という記事なのに**タイトル詐欺もいいとこ**ですが，こちらの方が簡潔に記述できます（コメント欄で@tadsanさんに教えていただきました）。

```php:（コメント欄からコピペ）
$array = [
  2 => [ 'id' => 111, 'name' => 'suzuki', 'age' => 21 ],
  3 => [ 'id' => 222, 'name' => 'sato',   'age' => 34 ],
  5 => [ 'id' => 333, 'name' => 'tanaka', 'age' => 55 ],
];

var_dump(array_map(fn($a) => $a['name'], $array));
```

上記のコードの配列操作部分を抜き出すと以下のようになります。

```php:（コメント欄からコピペ）
array_map(fn($a) => $a['name'], $array);
```

`array_map`の第1引数の`fn($a) => $a['name']`の部分がPHP7.4で導入された「アロー関数」なので，7.4より前の環境では「無名関数」を使って置き換えることになります。

```php:php
array_map( function ($a) { return $a['name']; }, $array );
```

- **メリット**　：アロー関数が使える環境では簡潔に記述できる
- **デメリット**：ワンライナーで無名関数を使う場合には少し可読性が下がる

<br>
###方法③`foreach`を使う
「**`foreach`を使わずに済むように`array_column`があるのでは？**」と言われればそれまでなのですが，方法②で`array_column`を使わなかったので一応。

```php:php
$array = [
  2 => [ 'id' => 111, 'name' => 'suzuki', 'age' => 21 ],
  3 => [ 'id' => 222, 'name' => 'sato',   'age' => 34 ],
  5 => [ 'id' => 333, 'name' => 'tanaka', 'age' => 55 ],
];

$result = [];
foreach ( $array as $key => $values ) {
  $result[ $key ] = $values['name'];
}
var_export( $result );
// array (
//   2 => 'suzuki',
//   3 => 'sato',
//   5 => 'tanaka',
// )
```

- **メリット**　：一見して何をやっているか分かりやすい
- **デメリット**：一時変数が必要，コードも複数行にわたる


##まとめ
**方法②の`array_map`とアロー関数を使ったワンライナーが一番タイプ数も少なくおすすめ**です。思想信条上の理由でクロージャを使いたくない方は，

- 1行にまとめたければ**方法①**
- だれが見ても分かりやすいコードにしたければ**方法③**

が良いと思います。記事タイトルにそぐわない結論になってしまいましたが。

##蛇足
`array_column`はPHP5.5から導入された関数ですが，よくよく調べてみると「第3引数が未指定の場合に元配列のキーを維持する案」がリリース前に出ており，[実際にプルリクエストも出されていた](https://github.com/php/php-src/pull/331)みたいですが，議論の末に見送られた経緯があるようです。こんな記事を書いといてアレですが，元配列のキーを**維持したくない**場合の方が多いはずなので，これは見送られて良かったと思われます。

それとは別に，[「第3引数に true が指定された場合に元配列のキーを維持する案」](https://marc.info/?l=php-internals&m=135802311120268)もあったようで，この案についてはリリース後もたまにリクエストがあがっているみたいです（[PHPで実装された array_column リポジトリの issue ](https://github.com/ramsey/array_column/issues/3)とか）。

ただ，PHP公式サイト上の[このページ](https://bugs.php.net/bug.php?id=66435)（←何故かつながりづらい）では，「第3引数に true を指定する案」や「第4引数を追加する案」などのいかなる変更も既存の予想される動作を破壊し得るとして，`array_column`の実装者である ramsey さん自身がリクエストを close しており，キーを維持するための機能追加が行われることは今後まず無さそうです。

##参考

[array_column - PHP.net](https://www.php.net/manual/ja/function.array-column.php)[^1]


[^1]: [`array_column`](https://www.php.net/manual/ja/function.array-column.php)の公式ページに「[第3引数に -1 を渡すと元の配列のキーを維持する関数](https://www.php.net/manual/ja/function.array-column.php#123045)」が掲載されています。この関数をコピペして使えば，`array_column_ext( $array,'name', -1 )`のように簡潔な記述ができます（どんな場合でも使えるかどうかは検証してないので分かりません）。