【PHP 初級】配列おさらい➁ ～取得・検索・その他～

[【PHP 初級】配列おさらい➀ ～宣言・初期化・追加・結合・削除～](https://qiita.com/gksdyd88/items/6b522709422e14dae2ab)の続きです。
今回は配列から値を取得、検索する方法など、もう少し実務的な内容をまとめます。

## 値の取得
配列から値を取り出す色んな方法を見てみます。
### キーを指定して値を取り出す
どの言語も同じように、数値やキーを指定して値を取り出します。

```php
php > $fruits = ['apple', 'grape', 'banana'];

php > var_export($fruits[0]);
'apple'
```
```php
php > $colors = ['C1' => 'red', 'C2' => 'purple', 'C3' => 'green'];

php > var_export($colors['C1']);
'red'
```

### すべての値を取り出す
#### array_values
https://www.php.net/manual/ja/function.array-values.php
>array_values (配列)
戻り値：配列

`array_values`は配列のすべての値を取り出し、数値のインデックス番号を付けた配列を返します。
[【PHP 初級】配列おさらい➀ ～宣言・初期化・追加・結合・削除～](https://qiita.com/gksdyd88/items/6b522709422e14dae2ab)で紹介したように、配列の値の削除を行った後、インデックス番号を詰め直すのに利用したりします。
また`array_values`に連想配列を渡せば、配列に変換することが出来ます。

```php
php > $colors = ['C1' => 'red', 'C2' => 'purple', 'C3' => 'green'];

php > var_export(array_values($colors));
array (
  0 => 'red',
  1 => 'purple',
  2 => 'green',
)
```

### 多次元配列からキーを指定して値を取り出す
#### array_column
https://www.php.net/manual/ja/function.array-column.php
>array_column (配列, 指定するキー)
戻り値：配列

`array_column`は個人的にかなり良く使うメソッドなので、ぜひ使い方を身に付けてみてください。
`array_column`は多次元配列のキーを指定し、全ての値を配列で返します。

```php
php > $students = [
php >     ['class' => 1, 'name' => 'tanaka', 'age' => 15],
php >     ['class' => 2, 'name' => 'yamamoto', 'age' => 16],
php >     ['class' => 1, 'name' => 'honda', 'age' => 16],
php >     ['class' => 3, 'name' => 'inoue', 'age' => 17],
php > ];

// 学生の名前を取得
php > $students_name = array_column($students, 'name');
php > var_export($students_name);
array (
  0 => 'tanaka',
  1 => 'yamamoto',
  2 => 'honda',
  3 => 'inoue',
)
```

また第3引数を指定して、特定の値をキーにした多次元配列を生成することもできます。
>array_column (配列, 指定するキー、新しい配列のキーとして指定するキー)
戻り値：配列

言葉が分かりづらいですが、例を見ればすぐに理解できると思います。

```php
php > $students = [
php >     ['class' => 1, 'name' => 'tanaka', 'age' => 15],
php >     ['class' => 2, 'name' => 'yamamoto', 'age' => 16],
php >     ['class' => 1, 'name' => 'honda', 'age' => 16],
php >     ['class' => 3, 'name' => 'inoue', 'age' => 17],
php > ];

// クラス毎に名前を取得する
php > $name_by_class = array_column($students, 'name', 'class');
php > var_export($name_by_class);
array (
  1 => 'honda',
  2 => 'yamamoto',
  3 => 'inoue',
)
```
注意点は**キーが重複する場合、後ろの値で上書き**されます。
そのため、クラス1はhondaくんが取得されます。

名前だけでなく、学生の情報が全て取得したい場合は第2引数に`null`を指定します。

```php
php > $students = [
php >     ['class' => 1, 'name' => 'tanaka', 'age' => 15],
php >     ['class' => 2, 'name' => 'yamamoto', 'age' => 16],
php >     ['class' => 1, 'name' => 'honda', 'age' => 16],
php >     ['class' => 3, 'name' => 'inoue', 'age' => 17],
php > ];

// クラス毎に学生の情報を取得する
php > $student_by_class = array_column($students, null, 'class');
php > var_export($student_by_class);
array (
  1 =>
  array (
    'class' => 1,
    'name' => 'honda',
    'age' => 16,
  ),
  2 =>
  array (
    'class' => 2,
    'name' => 'yamamoto',
    'age' => 16,
  ),
  3 =>
  array (
    'class' => 3,
    'name' => 'inoue',
    'age' => 17,
  ),
)
```
こちらもクラス1は重複するため、hondaくんの情報のみ取得されています。

### 多次元配列のキーでグルーピングする
上書きしないでクラス毎の全員の情報が取得したい場合は、次のような自作メソッドが必要になります。

```php
public function groupBy($list, $key) {
    $ret = array();
    if (is_array($list)) {
        foreach ($list as $val) {
            $ret[$val[$key]][] = $val;
        }
    }

    return $ret;
}
```
Utilメソッドとして定義し、どこからでも使えるようにしたら良いです。
グルーピングした結果は以下のようになります。

```php
php > $students = [
php >     ['class' => 1, 'name' => 'tanaka', 'age' => 15],
php >     ['class' => 2, 'name' => 'yamamoto', 'age' => 16],
php >     ['class' => 1, 'name' => 'honda', 'age' => 16],
php >     ['class' => 3, 'name' => 'inoue', 'age' => 17],
php > ];

// クラス毎に学生をグループ化する
php > $group_by_class = groupBy($students, 'class');
php > var_export($group_by_class);
array (
  1 =>
  array (
    0 =>
    array (
      'class' => 1,
      'name' => 'tanaka',
      'age' => 15,
    ),
    1 =>
    array (
      'class' => 1,
      'name' => 'honda',
      'age' => 16,
    ),
  ),
  2 =>
  array (
    0 =>
    array (
      'class' => 2,
      'name' => 'yamamoto',
      'age' => 16,
    ),
  ),
  3 =>
  array (
    0 =>
    array (
      'class' => 3,
      'name' => 'inoue',
      'age' => 17,
    ),
  ),
)
```

## キーの取得
次はキーを取得する方法を見てみましょう。
### 配列の全てのキーを取得する
#### array_keys
https://www.php.net/manual/ja/function.array-keys.php
>array_keys (配列)
戻り値：配列

引数に渡した配列の全てのキーを配列にて返します。

```php
php > $fruits_color = [
php >     'apple' => 'red',
php >     'banana' => 'yellow',
php >     'orange' => 'orange',
php >     'cherry' => 'red',
php > ];

php > var_export(array_keys($fruits_color));
array (
  0 => 'apple',
  1 => 'banana',
  2 => 'orange',
  3 => 'cherry',
)
```

### 値を指定してキーを取得する
#### array_keys
https://www.php.net/manual/ja/function.array-keys.php
>array_keys (配列, 指定する値, 型比較を行うか)
戻り値：配列

指定した値に該当するキーを取得するには`array_keys`に第2引数を渡して使用します。
第3引数には値を検索する際に厳密な比較`===`を行うかどうかを`true`,`false`で指定します。
常に`true`を指定することをお勧めします。

```php
php > $fruits_color = [
php >     'apple' => 'red',
php >     'banana' => 'yellow',
php >     'orange' => 'orange',
php >     'cherry' => 'red',
php > ];

php > $red_fruits = array_keys($fruits_color, 'red', true);
php > var_export($red_fruits);
array (
  0 => 'apple',
  1 => 'cherry',
)
```
もし第3引数に`true`を指定しない場合、以下のような想定外の動作をする可能性があります。

```php
php > $numbers = [
php >     'zero' => 0,
php >     'one' => 1,
php >     'two' => 2,
php > ];

// 何も取得されないと思いきや、'zero'が取得される
php > $number = array_keys($numbers, 'a');
php > var_export($number);
array (
  0 => 'zero',
)
```
第3引数を指定しないと値の検索に緩やかな比較`==`を行います。
PHPは型が異なる値同士を比較する際に、暗黙的な型変換を行います。
そのため数値と文字列を比較しようとすると、文字列は0に変換されてしまいます。

```php
php > var_export((int) 'a');
0
```
なので上記のように第2引数に'a'を指定しても、実際には0に変換されてしまうので、'zero'が取得されてしまうのです。
このような想定外の動作はバグを引き起こす可能性があるため、避けるべきでしょう。

## 値の検索
### 値を指定して存在するか検索する
#### array_search
https://www.php.net/manual/ja/function.array-search.php
>array_search(検索する値, 配列, 型比較を行うか)
戻り値：キーもしくはfalse

値が見つかった場合、該当のキーを返します。
見つからなかった場合は`false`を返します。

`array_keys`は値を指定した場合、該当するキーを全て配列で返しますが、`array_search`は1つのキーを数値か文字列にて返します。
そのため1つ以上の値が見つかる場合は先に見つかった方のキーを返します。

```php
php > $foods = ['ramen', 'sushi', 'curry', 'udon', 'karaage', 'sushi'];

// 寿司を検索。寿司は2つあるため、前方のキーが取得される。
php > $sushi_key = array_search('sushi', $foods, true);
php > var_export($sushi_key);
1

// ピザを検索。値が見つからないためfalseを返す。
php > $pizza_key = array_search('pizza', $foods, true);
php > var_export($pizza_key);
false
```
`array_search`も第3引数に`true`を指定して厳密な比較を行うようにしましょう。

#### in_array
https://www.php.net/manual/ja/function.in-array.php
>in_array(検索する値, 配列, 型比較を行うか)
戻り値：trueもしくはfalse

使い方は`array_search`と同じですが、値が見つかった場合にキーではなく`true`を返します。
第3引数にはもちろん`true`を指定します。

```php
php > $foods = ['ramen', 'sushi', 'curry', 'udon', 'karaage', 'sushi'];

php > $sushi_key = in_array('sushi', $foods, true);
php > var_export($sushi_key);
true

php > $pizza_key = in_array('pizza', $foods, true);
php > var_export($pizza_key);
false
```
`array_search`と`in_array`の使い分けは、
値を検索して存在する場合、その値を用いて何らかの処理をしたい時は`array_search`を

```php
$key = array_search('val', $list, true);
if ($key !== false) {
    $value = $list[$key];
    ... // 値を使って処理を行う
}
```
値を検索して存在するかしないかで何らかの処理をしたい場合は`in_array`を使うイメージです。

```php
if (in_array('val', $list, true)) {
    ... // 存在する場合の処理
} else {
    ... // 存在しない場合の処理
}
```

## キーの検索
### キーを指定して存在するか検索する
#### array_key_exists
https://www.php.net/manual/ja/function.array-key-exists.php
>array_key_exists (検索するキー, 配列)
戻り値：trueもしくはfalse

第1引数に渡したキーが存在すれば`true`を返します。

```php
php > $fruits = [
php >     'name' => 'apple', 'color' => 'red',
php >     'name' => 'banana', 'color' => 'yellow',
php >     'name' => 'melon', 'color' => 'green',
php > ];

php > var_export(array_key_exists('name', $fruits));
true
php > var_export(array_key_exists('price', $fruits));
false
```
`isset`や`empty`を使っても`array_key_exists`と同じようなことができます。

#### isset
https://www.php.net/manual/ja/function.isset.php
>isset (変数)
戻り値：trueもしくはfalse

変数が宣言されていて、かつ`null`でなければ`true`を返します。

```php
php > $fruits = [
php >     'name' => 'apple', 'color' => 'red',
php >     'name' => 'banana', 'color' => 'yellow',
php >     'name' => 'melon', 'color' => 'green',
php > ];

php > var_export(isset($fruits['name']));
true
php > var_export(isset($fruits['price']));
false
```

#### empty
https://www.php.net/manual/ja/function.empty.php
>empty (変数)
戻り値：trueもしくはfalse

変数が宣言されていないか、空であれば`true`を返します。
`array_key_exist`や`isset`とは逆にキーが**存在しなければ**`true`を返します。
同じ使い方をしたい場合は必ず`!`を付けるようにしましょう。

```php
php > $fruits = [
php >     'name' => 'apple', 'color' => 'red',
php >     'name' => 'banana', 'color' => 'yellow',
php >     'name' => 'melon', 'color' => 'green',
php > ];

php > var_export(!empty($fruits['name']));
true
php > var_export(!empty($fruits['price']));
false
```

#### array_key_exists、isset、emptyの違い
`array_key_exists`はキーが定義されているかどうかのみを判断します。
`isset`はキーが定義されているか、また値が`null`ではないかを判断します。
`empty`はキーが提起されているか、また値が空ではないかを判断します。

いくつかの違いをまとめると以下のようになります。

|key/value        |array_key_exists  |isset   |!empty  |
|:----------------|:----------------:|:------:|:------:|
|'a' => undefined |false             |false   |false   |
|0 => NULL        |**true**          |false   |false   |
|1 => ''          |**true**          |**true**|false   |
|2 => 0           |**true**          |**true**|false   |
|3 => 'val'       |**true**          |**true**|**true**|

```php
// array_key_exists
php > var_export(array_key_exists('a', $list));
false
php > var_export(array_key_exists(0, $list));
true
php > var_export(array_key_exists(1, $list));
true
php > var_export(array_key_exists(2, $list));
true
php > var_export(array_key_exists(3, $list));
true

// isset
php > var_export(isset($list['a']));
false
php > var_export(isset($list[0]));
false
php > var_export(isset($list[1]));
true
php > var_export(isset($list[2]));
true
php > var_export(isset($list[3]));
true

// empty
php > var_export(!empty($list['a']));
false
php > var_export(!empty($list[0]));
false
php > var_export(!empty($list[1]));
false
php > var_export(!empty($list[2]));
false
php > var_export(!empty($list[3]));
true
```

キーが存在するかチェックしたい場合は`array_key_exists`を、
キーが存在して意味のある値が入っていることを確認したい場合は`isset`や`empty`を使いましょう。

## その他、配列に関する色々
### JAVAのstreamぽい書き方もできる
詳細は割愛しますが、JAVAのstreamのように配列の全要素を回しながら処理する色んな関数があります。
[array_walk](https://www.php.net/manual/ja/function.array-walk.php)
>array_walk (配列, コールバック関数, コールバック関数の引数)
戻り値：true

配列を回してコールバック関数の処理を行います。
コールバック関数に渡す第1引数は配列の値、第2引数は配列のキーになります。
コールバック関数で第3引数を使用したい場合は`array_walk`に第3引数を指定します。
JAVAの`stream.forEach`みたいなイメージです。

[array_filter](https://www.php.net/manual/ja/function.array-filter.php)
>array_filter (配列, コールバック関数, モード)
戻り値：配列

配列を回してコールバック関数でフィルタリングした結果を配列で返します。
第3引数ではコールバック関数に何を引数として渡すかを指定します。
デフォルトだと配列の値を引数として渡します。
JAVAの`stream.filter`に値します。

[array_map](https://www.php.net/manual/ja/function.array-map.php)
>array_map (コールバック関数, 配列...)
戻り値：配列

配列を回してコールバック関数の処理を行います。
コールバック関数の処理を通った結果を配列で返します。
コールバック関数に引数で渡す配列は複数指定することが出来ます。
JAVAの`stream.map`に似ています。


### 最後の要素の後にはカンマを付ける
これは賛否両論があるようですが
・**1行**で記述する場合は最後にカンマを**付けない**
・**複数行**で技術する場合は最後にカンマを**付ける**
が一般的のようです。

[PHP公式サイト](https://www.php.net/manual/ja/language.types.array.php)
>配列を一行で定義する場合は、ふつうは最後のカンマを省略します。
つまり、 array(1, 2) のほうが array(1, 2, ) よりおすすめだということです。 
しかし複数行で定義する場合は、最後のカンマをつけることが一般的です。 
そうしておけば、配列の最後に要素を追加するのが容易になるからです。

[Zend framework](https://framework.zend.com/manual/2.4/en/ref/coding.standard.html#arrays)
>we encourage using a trailing comma for the last item in the array; this minimizes the impact of adding new items on successive lines, and helps to ensure no parse errors occur due to a missing comma.

```php
$sampleArray = array(1, 2, 3, 'Zend', 'Studio'); // 最後にカンマを付けない
```

```php
$sampleArray = array(
    1, 2, 3, 'Zend', 'Studio',
    $a, $b, $c,
    56.44, $d, 500, // 最後にカンマを付ける
);
```