【PHP】開発現場で必ず使う配列関数のすべて

## はじめに
webサービスを構築していく中で必要な知識を備忘録として残しております。

今回は `配列関数` について深堀りしていきたいと思います。
※随時writeしていきます。


## 現場でよく使う配列関数
<table>
  <thead>
    <tr>
      <th>配列関数</th> 
      <th>説明</th>
      <th>使用頻度</th>
    </tr>
  </thead>
  <tr>
    <td> in_array </td>
    <td>配列の要素に特定の値を含むものがあるか</td>
    <td>★★★★★</td>
  </tr>
  <tr>
    <td> count </td>
    <td>配列の要素の数を返す</td>
    <td>★★★★★</td>
  </tr>
  <tr>
    <td> array_merge </td>
    <td>配列同士を結合(マージ)する</td>
    <td>★★★★</td>
  </tr>
  <tr>
    <td> array_push </td>
    <td>配列の末尾に要素を追加する</td>
    <td>★★★</td>
  </tr>
  <tr>
    <td> array_chunk </td>
    <td>配列を指定した要素を持つ配列に分解する</td>
    <td>★★★</td>
  </tr>
  <tr>
    <td> array_column </td>
    <td>配列から特定単一カラムの配列を取得する</td>
    <td>★★★</td>
  </tr>
</table>

## in_array
### 概要
配列の要素に特定の値を含むものがあるかをチェックする関数

第一引数：配列内で検索される値
第二引数：検索対象の配列
第三引数：型チェックまで実施するかどうか（比較を厳密に行うかどうか）
戻り値：bool (true・false)
```php
in_array(mixed $needle, array $haystack, bool $strict = false): bool
```
### 使用例
```php
<?php
$colors = ['red', 'blue', 'orange'];

// -- 1
if(in_array('blue', $colors, true)) {
    echo 'blueは配列内に存在します';
} else {
    echo 'blueは配列内に存在しません';
}

// -- 2
if(in_array('black', $colors, true)) {
    echo 'blackは配列内に存在します';
} else {
    echo 'blackは配列内に存在しません';
}
?>
```

```php
1 : blueは配列内に存在します
2 : blackは配列内に存在しません
```
### 多次元配列の場合
後に説明する `array_column` を使用し判定処理を実施
```php
<?php
$colors = [
    ['color' => 'red', 'number' => '1'],
    ['color' => 'blue', 'number' => '2'],
    ['color' => 'orange', 'number' => '3']
];

// -- 1
if(in_array('blue', array_column($colors, 'color'), true)) {
    echo 'blueは配列内に存在します';
} else {
    echo 'blueは配列内に存在しません';
}

// -- 2
if(in_array('black', array_column($colors, 'color'), true)) {
    echo 'blackは配列内に存在します';
} else {
    echo 'blackは配列内に存在しません';
}
?>
```
```php
1 : blueは配列内に存在します
2 : blackは配列内に存在しません
```


### 代替ロジックとのパフォーマンス比較
`in_array` VS `array_search` VS `isset`

* `in_array`
```php
<?php
// 検証用10万テストデータ生成
$tests = [];
for( $i = 0; $i < 100000; ++$i ){
  $tests[] = 'test' . '_'.  $i;
}

$start = microtime(true);

if(in_array('test_99999', $tests, true)) {
    $finished = true;
};

$end = microtime(true);

echo sprintf("%.6f",($end - $start)) . '秒';
?>
```

```
0.000694秒
```
* `array_search`
```php
<?php
// 検証用10万テストデータ生成
$tests = [];
for( $i = 0; $i < 100000; ++$i ){
  $tests[] = 'test' . '_'.  $i;
}

$start = microtime(true);

$key = array_search('test_99999', $tests);

if ($key !== false) {
    $finished = true;
} 

$end = microtime(true);

echo sprintf("%.6f",($end - $start)) . '秒';
?>
```
```
0.000582秒
```

* `isset`
```php
<?php
// 検証用10万テストデータ生成
$tests = [];
for( $i = 0; $i < 100000; ++$i ){
  $tests[] = 'test' . '_'.  $i;
}

$start = microtime(true);

// issetを使用するにはキーと値を反転させる必要がある
$tests = array_flip($tests);

if(isset($tests['test_99999'])) {
    $finished = true;
};

$end = microtime(true);

echo sprintf("%.6f",($end - $start)) . '秒';
?>
```
```
0.004728秒
```
* 結果
`in_array` と `array_search` は同等のパフォーマンス結果となった。
なので、パフォーマンス的にはどちらでも良いと思いますが、戻り値がそれぞれ違うため、状況に応じて使い分けが必要だと思います。
（`in_array` : bool、`array_search` : key値 or false）
`isset`で判定する場合 `array_flip`をする必要があるため、その分遅くなっていた。

## count
### 概要
配列やオブジェクトの要素数を数えるために使用する関数

第一引数：要素数を数える対象の配列やカウンタブルなオブジェクト
第二引数：デフォルトは `COUNT_NORMAL` です。`COUNT_RECURSIVE` を指定すると、多次元配列の場合に再帰的に要素を数えます
戻り値：int
```php
count(mixed $array_or_countable, int $mode = COUNT_NORMAL): int
```
### 使用例
```php
$colors = ['red', 'blue', 'orange'];
$count = count($colors);
echo "要素数: $count";
```
```
要素数: 3
```

### 多次元配列の場合
```php
<?php
$colors = [
    ['color' => 'red', 'number' => '1'],
    ['color' => 'blue', 'number' => '2'],
    ['color' => 'orange', 'number' => '3'],
    ['color' => 'black', 'number' => '4']
];
$count = count($colors);
echo "要素数: $count";
?>
```
```
要素数: 4
```
* mode = `COUNT_RECURSIVE`
```php
<?php
$colors = [
    ['color' => 'red', 'number' => '1'],
    ['color' => 'blue', 'number' => '2'],
    ['color' => 'orange', 'number' => '3'],
    ['color' => 'black', 'number' => '4']
];
$count = count($colors, COUNT_RECURSIVE);
echo "要素数: $count";
?>
```
```
要素数: 12
```
要素数8 + 配列数4 で12とカウントされます。使い道はあまりなさそう。

### 代替ロジックとのパフォーマンス比較
`count` VS `sizeof`

* `count`
```php
<?php
// 検証用10万テストデータ生成
$tests = [];
for( $i = 0; $i < 100000; ++$i ){
  $tests[] = 'test' . '_'.  $i;
}

$start = microtime(true);

$count = count($tests);

$end = microtime(true);

echo sprintf("%.6f",($end - $start)) . '秒';
?>
```

```
0.000001秒
```

* `sizeof`
```php
<?php
// 検証用10万テストデータ生成
$tests = [];
for( $i = 0; $i < 100000; ++$i ){
  $tests[] = 'test' . '_'.  $i;
}

$start = microtime(true);

$count = sizeof($tests);

$end = microtime(true);

echo sprintf("%.6f",($end - $start)) . '秒';
?>
```

```
0.000006秒
```
単純に配列の要素数を取得するのは `count` がパフォーマンスが良い結果がでました。

## array_merge
### 概要
複数の配列を結合するための関数

第一引数：結合する最初の配列
第二引数：結合する他の配列（複数指定可能）
戻り値：配列
```php
array_merge(array $array1, array ...$arrays): array
```
### 使用例
```php
<?php
$array1 = ["red", "blue"];
$array2 = ["orange"];

$mergedArray = array_merge($array1, $array2);

print_r($mergedArray);
?>
```
```
Array
(
    [0] => red
    [1] => blue
    [2] => orange
)
```

### 連想配列の場合
```php
<?php
$array1 = ["r" => "red", "b" => "blue"];
$array2 = ["o" => "orange"];
$array3 = ["w" => "white"];

$mergedArray = array_merge($array1, $array2, $array3);

print_r($mergedArray);
?>
```
```
Array
(
    [r] => red
    [b] => blue
    [o] => orange
    [w] => white
)
```

* 重複キーがある場合

```php
<?php
$array1 = ["r" => "red", "b" => "blue"];
$array2 = ["o" => "orange"];
$array3 = ["b" => "black"];

$mergedArray = array_merge($array1, $array2, $array3);

print_r($mergedArray);
?>
```
```
Array
(
    [r] => red
    [b] => black
    [o] => orange
)
```
`キー:b` は二つ存在するが、blackで上書きされてしまう。

### 多次元配列の場合
```php
<?php
$colors = [
    ['color' => 'red', 'number' => '1'],
    ['color' => 'blue', 'number' => '2'],
    ['color' => 'orange', 'number' => '3'],
    ['color' => 'black', 'number' => '4']
];

$colors2 = [
    ['color' => 'white', 'number' => '5'],
    ['color' => 'pink', 'number' => '6'],
];

$mergedArray = array_merge($colors, $colors2);

print_r($mergedArray);
?>
```
```
Array
(
    [0] => Array
        (
            [color] => red
            [number] => 1
        )

    [1] => Array
        (
            [color] => blue
            [number] => 2
        )

    [2] => Array
        (
            [color] => orange
            [number] => 3
        )

    [3] => Array
        (
            [color] => black
            [number] => 4
        )

    [4] => Array
        (
            [color] => white
            [number] => 5
        )

    [5] => Array
        (
            [color] => pink
            [number] => 6
        )
)
```

### 代替ロジックとのパフォーマンス比較
`array_merge` VS `+ 演算子` VS `array_merge_recursive`

* `array_merge`
```php
<?php
// 検証用10万テストデータ1生成
$tests = [];
for( $i = 0; $i < 100000; ++$i ){
  $tests[] = ['test' => $i];
}

// 検証用10万テストデータ2生成
$tests2 = [];
for( $i = 0; $i < 100000; ++$i ){
  $tests2[] = ['test2' => $i];
}

$start = microtime(true);

$mergedArray = array_merge($tests,$tests2);

$end = microtime(true);

echo sprintf("%.6f",($end - $start)) . '秒';
?>
```

```
0.004547秒
```

* `+ 演算子`
```php
<?php
// 検証用10万テストデータ1生成
$tests = [];
for( $i = 0; $i < 100000; ++$i ){
  $tests[] = ['test' => $i];
}

// 検証用10万テストデータ2生成
$tests2 = [];
for( $i = 0; $i < 100000; ++$i ){
  $tests2[] = ['test2' => $i];
}

$start = microtime(true);

$mergedArray = $tests + $tests2;

$end = microtime(true);

echo sprintf("%.6f",($end - $start)) . '秒';
?>
```

```
0.003017秒
```

* `array_merge_recursive`
```php
<?php
// 検証用10万テストデータ1生成
$tests = [];
for( $i = 0; $i < 100000; ++$i ){
  $tests[] = ['test' => $i];
}

// 検証用10万テストデータ2生成
$tests2 = [];
for( $i = 0; $i < 100000; ++$i ){
  $tests2[] = ['test2' => $i];
}

$start = microtime(true);

$mergedArray = array_merge_recursive($tests,$tests2);

$end = microtime(true);

echo sprintf("%.6f",($end - $start)) . '秒';
?>
```

```
0.004963秒
```
すべて同等のパフォーマンスが出る結果となった。
ただし、今回はキー指定なしの多次元配列で検証したが、重複ありだと `array_merge_recursive`のパフォーマンスが著しく落ちる結果も見られた。
また、キー重複ありの場合、`array_merge`は上書きするに対して、`+ 演算子`は最初の配列の値が優先されます。（先勝ち）
その特性も踏まえての使い分けが必要です。


## array_push
### 概要
配列の末尾に一つ以上の要素を追加するための関数

第一引数：要素を追加する対象の配列（参照渡し）
第二引数：追加する要素。複数の要素を一度に追加することができます
戻り値：int (配列要素数)
```php
array_push(array &$array, mixed $value1, mixed $value2, ...): int
```
### 使用例
```php
<?php
$colors = ["red", "blue"];

array_push($colors, "orange", "black");

print_r($colors);
?>
```
```
Array
(
    [0] => red
    [1] => blue
    [2] => orange
    [3] => black
)
```


### 代替ロジックとのパフォーマンス比較
`array_push` VS `[] 演算子`

* `array_push`
```php
<?php
// 検証用10万テストデータ1生成
$tests = [];
for( $i = 0; $i < 100000; ++$i ){
  $tests["test_1_$i"] = 'test' . '_'.  $i;
}


$start = microtime(true);

$results = [];
foreach ($tests as $test) {
    array_push($results, $test);
}

$end = microtime(true);

echo sprintf("%.6f",($end - $start)) . '秒';
?>
```

```
0.006505秒
```

* `[] 演算子`
```php
<?php
// 検証用10万テストデータ1生成
$tests = [];
for( $i = 0; $i < 100000; ++$i ){
  $tests["test_1_$i"] = 'test' . '_'.  $i;
}


$start = microtime(true);

$results = [];
foreach ($tests as $test) {
    $results[] = $test;
}

$end = microtime(true);

echo sprintf("%.6f",($end - $start)) . '秒';
?>
```

```
0.005865秒
```


`array_push` と `[] 演算子` が同等のパフォーマンスだが、`[] 演算子` のほうが速い結果となった。
検証件数より多いデータ量だと差は開いていくので、パフォーマンス的には`[] 演算子`を使用するほうが良いと言える。




## array_chunk
### 概要
配列を指定されたサイズで分割するための関数

第一引数：分割する対象の配列
第二引数：各チャンクのサイズを指定します
第三引数：キーを保持するかどうかを指定するフラグ。デフォルトは false で、キーが再インデックス化されます
戻り値：配列
```php
array_chunk(array $input, int $size, bool $preserve_keys = false): array
```
### 使用例
```php
<?php
$colors = ['red','blue','orange','black','white','pink','yellow','green','brown'];

$chunks = array_chunk($colors, 3);

print_r($chunks);
?>
```
```
Array
(
    [0] => Array
        (
            [0] => red
            [1] => blue
            [2] => orange
        )

    [1] => Array
        (
            [0] => black
            [1] => white
            [2] => pink
        )

    [2] => Array
        (
            [0] => yellow
            [1] => green
            [2] => brown
        )

)
```
* 多次元配列の場合
```php
$colors = [
    ['color' => 'red', 'number' => '1'],
    ['color' => 'blue', 'number' => '2'],
    ['color' => 'orange', 'number' => '3'],
    ['color' => 'black', 'number' => '4'],
    ['color' => 'white', 'number' => '5'],
    ['color' => 'pink', 'number' => '6'],
    ['color' => 'yellow', 'number' => '7'],
    ['color' => 'green', 'number' => '8'],
    ['color' => 'brown', 'number' => '9'],
];

$chunks = array_chunk($colors, 3);

print_r($chunks);
```
```
Array
(
    [0] => Array
        (
            [0] => Array
                (
                    [color] => red
                    [number] => 1
                )

            [1] => Array
                (
                    [color] => blue
                    [number] => 2
                )

            [2] => Array
                (
                    [color] => orange
                    [number] => 3
                )

        )

    [1] => Array
        (
            [0] => Array
                (
                    [color] => black
                    [number] => 4
                )

            [1] => Array
                (
                    [color] => white
                    [number] => 5
                )

            [2] => Array
                (
                    [color] => pink
                    [number] => 6
                )

        )

    [2] => Array
        (
            [0] => Array
                (
                    [color] => yellow
                    [number] => 7
                )

            [1] => Array
                (
                    [color] => green
                    [number] => 8
                )

            [2] => Array
                (
                    [color] => brown
                    [number] => 9
                )

        )

)
```




## array_column
### 概要
多次元配列から指定した列（キー）の値だけを抽出して新しい配列を生成するための関数

第一引数：対象の多次元配列
第二引数：抽出したい列のキー。これは、連想配列の場合はキー、数値添字の場合はインデックスとなります
第三引数：新しい配列のインデックスとして使用する列のキー。省略すると数値添字が使用されます
戻り値：配列
```php
aarray_column(array $input, mixed $column_key, mixed $index_key = null): array
```
### 使用例
```php
<?php
$colors = [
    ['color' => 'red', 'number' => '1'],
    ['color' => 'blue', 'number' => '2'],
    ['color' => 'orange', 'number' => '3'],
    ['color' => 'black', 'number' => '4']
];

$color = array_column($colors, 'color');

print_r($color);
?>
```
```
Array
(
    [0] => red
    [1] => blue
    [2] => orange
    [3] => black
)
```
### 代替ロジックとのパフォーマンス比較
`array_column` VS `array_map` VS `foreach`

* `array_column`
```php
<?php
// 検証用10万テストデータ生成
$tests = [];
for( $i = 0; $i < 100000; ++$i ){
  $tests[] = ['number' => $i , 'key' => 'test'. '_' .$i];
}


$start = microtime(true);

$color = array_column($tests, 'number');

$end = microtime(true);

echo sprintf("%.6f",($end - $start)) . '秒';
?>
```
```
0.004329秒
```

* `foreach`
```php
<?php
// 検証用10万テストデータ生成
$tests = [];
for( $i = 0; $i < 100000; ++$i ){
  $tests[] = ['number' => $i , 'key' => 'test'. '_' .$i];
}


$start = microtime(true);

$numbers = [];

foreach ($tests as $test) {
    $numbers[] = $test['number'];
}

$end = microtime(true);

echo sprintf("%.6f",($end - $start)) . '秒';
?>
```
```
0.014227秒
```

* `array_map`
```php
<?php
// 検証用10万テストデータ生成
$tests = [];
for( $i = 0; $i < 100000; ++$i ){
  $tests[] = ['number' => $i , 'key' => 'test'. '_' .$i];
}


$start = microtime(true);

$color = array_map(function($test) {
    return $test["number"];
}, $tests);

$end = microtime(true);

echo sprintf("%.6f",($end - $start)) . '秒';
?>
```
```
0.013517秒
```
`array_column` がパフォーマンスが良い結果となった。
特定の要件を満たす場合は、シンプルで効果的といえます。