PHP配列処理大全

競技プログラミングで配列を操作することが多いので内部関数をまとめました。
#目次
[配列の結合](#配列の結合)
[+演算子で結合する際の注意](#演算子で結合する際の注意)
[文字列を配列にする](#文字列を配列にする)
[配列を文字列にする](#配列を文字列にする)
[配列に値が存在するか確認する](#配列に値が存在するか確認する)
[配列の並べ替え](#配列の並べ替え)
[配列を逆順にする](#配列を逆順にする)
[配列の先頭に値を追加する](#配列の先頭に値を追加する)
[配列の末尾に値を追加する](#配列の末尾に値を追加する)
[配列の指定箇所に値を追加する](#配列の指定箇所に値を追加する)
[配列の指定箇所を取り除く](#配列の指定箇所を取り除く)
[配列の指定箇所を置き換える](#配列の指定箇所を置き換える)
[配列の一部を取り出す](#配列の一部を取り出す)
[配列の値を別の変数に代入する](#配列の値を別の変数に代入する)
[配列から条件に合うデータだけを抜き出して配列にする](#配列から条件に合うデータだけを抜き出して配列にする)
[配列を一括処理する](#配列を一括処理する)
[連想配列を値で並べ替える](#連想配列を値で並べ替える)
[連想配列を添え字で並び替える](#連想配列を添え字で並び替える)

#配列の結合
``array_merge()``関数か``array_merge_recursive()関数``、``+演算子``も使える

``array_merge()``関数では前の配列に後ろの配列を追加した配列を返す
数値添え字の配列を結合する際、前の配列と後ろの配列は上書きされないように結合される
結合結果の数値添え字は**振り直される**
**連想配列を結合する時に、添え字が重複した場合は後の配列で上書きされる**

``array_merge_recursive()``関数は``array_merge()``関数とほぼ同じ
**連想配列の結合を行う時に添字が重複した場合はネストを一段深くして値を残すところが異なる**

``+演算子``による結合は配列の種類にかかわらず、添字が重複した場合は前の配列を優先して結合する

```example.php
<?php
$arr1 = ['a'];
$arr2 = ['A', 10 => 'B'];
array_merge($arr1, $arr2);
/*
  0 => 'a',
  1 => 'A',
  2 => 'B'
 */
array_merge_recursive($arr1, $arr2);
/*
  0 => 'a',
  1 => 'A',
  2 => 'B'
 */
$arr1 + $arr2;
/*
  0 => 'a',
  10 => 'B'
 */
//-------------------------

$arr1 = ['a' => 1];
$arr2 = ['a' => 'A', 'b' => 'B'];

array_merge($arr1, $arr2);
/*
  'a' => 'A',
  'b' => 'B'
 */
array_merge_recursive($arr1, $arr2);
/*
  'a' => [0 => 1, 1 => 'A'],
  'b' => 'B'
 */
$arr1 + $arr2;
/*
  'a' => 1,
  'b' => 'B'
 */
```

#+演算子で結合する際の注意
``連想配列の添字が数字として判定できる場合は数値にキャストされるので
'1'と1は同じ添字として処理される``
#文字列を配列にする

```php
$target = 'a,b,c,d,e';
explode(',', $target); //['a', 'b', 'c', 'd', 'e']
```
#配列を文字列にする

```php
$target = ['a', 'b', 'c', 'd', 'e'];
implode(',', $target); //'a,b,c,d,e'
```
#配列に値が存在するか確認する
``in_array()``関数や、``array_search()``関数を使う
``in_array()``関数を使用した場合、返り値は``bool``で
``array_search()``関数を使用した場合、最初に見つけた要素の``添え字``を返し
存在しなかった場合は``false``を返す

```php:注意
array_search()関数の戻り値を判定するときは、===を必ず使う
==を使った場合、0がfalseと同じ値だと判断されてしまう可能性があるから
```

```php
<?php
$arr = ['a', 'b', 'c'];
in_array('a', $arr); //true
array_search('a', $arr); //0
```

#配列の並べ替え
``sort()``関数、``rsort()``関数を使う
``sort()``関数は昇順、``rsort()``関数は降順に並び替える
第二引数は省略可能だが、数値として並び替える``SORT_NUMERIC``か文字列として並び替える``SORT_STRING``のどちらかを指定する方が良い
これらの関数は与えられた配列を``直接操作``し、添字が数値で**振り直される**
返り値は``bool``
直感的にわかると思うのでコード省略

#配列を逆順にする
``array_reverse()``関数を使う
多次元配列の場合は**ネストの内側の配列**は並び替えてくれない
並び替えと同時に数値添え字を0から**振り直す**
元の数値の添字を保持しておきたい場合は``第二引数``に``true``を指定する
**連想配列の場合は第二引数を指定しても添字は振り直されない**

```php
<?php
$arr = ['a', 'b', 'c'];
array_reverse($arr); //[0 => 'c', 1 => 'b', 2 => 'a']
array_reverse($arr, true); //[2 => 'c', 1 => 'b', 0 => 'a']
//--------------------------------------------------

$arr = [
	'a' => 10,
	'b' => 20,
	'c' => 30,
];
array_reverse($arr); //['c' => 30, 'b' => 20, 'a' => 10]
array_reverse($arr, true); //同じ
//--------------------------------------------------

$arr = [
	10,
	[
		'A' => 1,
		'B' => 2,
		'C' => 3,
	],
	'ABC',
];
array_reverse($arr); //[0 => 'ABC', 1 => ['A' => 1, 'B' => 2, 'C' => 3], 2 => 10]
array_reverse($arr, true); //[2 => 'ABC', 1 => ['A' => 1, 'B' => 2, 'C' => 3], 0 => 10]
```
#配列の先頭に値を追加する
``array_unshift()``関数を使う
値を追加すると0からの連番で添字が**振り直される**
また配列を``直接``操作する
連想配列に対しても使えるが追加する要素の添字は0になる

```php
<?php
$arr = ['a', 'b', 'c'];
array_unshift($arr, 'added'); //[0 => 'added', 1 => 'a', 2 => 'b', 3 => 'c']
```
#配列の末尾に値を追加する
添字を指定せずに[]をつけて値を代入する

```php
<?php
$arr[] = 'abc';
```
連想配列の末尾に値を追加する場合は通常通り

```php
$arr['test'] = 'example';
```

``array_push()``関数を使うこともできるが関数を呼び出す分のコストがかかってしまうのであまりパフォーマンスが良くない
参考記事
https://qiita.com/kapitan/items/47d89449f23bc7f7c9bb

#配列の指定箇所に値を追加する
``array_splice()``関数を使う

```
array_splice($arr, $offset, 0, $value);
```
第二引数に**負の数**を指定すると``末尾``からの位置指定になる
第四引数で配列を指定すると複数の値を一気に追加できる
配列の数字添字は``振り直し``になって、配列は``直接``操作されます

```php
<?php
$arr = ['a', 'b', 'c'];
$value = 'added';
array_splice($arr, 1, 0, $value); //[0 => 'a', 1 => 'added', 2 => 'b', 3 => 'c']
```

#配列の指定箇所を取り除く
同様に``array_splice()``を使う
**配列、開始位置、要素数**を引数にとって返り値には取り除かれた値の配列が代入される

```php
$removed_value = array_splice($arr, $offset, $count);
```

```php
<?php
$arr = ['a', 'b', 'c'];
array_splice($arr, 1, 2); //['a']
```

#配列の指定箇所を置き換える
同様に``array_splice()``を使う
配列、開始位置、要素数、新しい値を引数にとって戻り値には抽出された値の配列が代入される

```php
$replaced_value = array_splice($arr, $offset, $count, $value);
```

```php
$arr = ['a', 'b', 'c'];
array_splice($arr, 1, 2, 'x'); //['a', 'x', 'x']
```
#配列の一部を取り出す
``array_slice()``関数を使う
``array_slice()``関数は配列の一部を切り出して、``新しい配列``を返り値として返す
ただ、**元の配列から要素が削除されてしまうことはない**のが``array_splice()``関数と違うところ！

```php
$sliced_arr = array_slice($arr, $offset[, $count]);
```
第三引数は省略可能

```php
<?php
$arr = ['a', 'b', 'c'];
$sliced_arr = array_slice($arr, 1); //['a']
#配列の値を別の変数に代入する
```
#配列から条件に合うデータだけを抜き出して配列にする
``array_filter()``関数を使う
指定した条件に合致する要素のみを**新しい配列**にして返す

```php
<?php
$filetered_arr = array_filter($arr, 'callback');

function callback($value): bool
{
	return true; //false
}
```
コールバック関数にはユーザー定義関数はもちろん、内部関数も使うことができる
ただ、関数で実現できない、言語構造を指定することはできない
例えば``isset()``とか

```php
<?php
$arr = range(2, 50);
function isOdd($value): bool
{
	if (!is_int($value)) return false;
	return ($value % 2 != 0);
}
function isEven($value): bool
{
	if (!is_int($value)) return false;
	return ($value % 2 == 0);
}
$odd_arr = array_filter($arr, 'isOdd');
$even_arr = array_filter($arr, 'isEven');
```
#配列を一括処理する
``array_walk()``関数か``array_map()``関数を使う

**``array_walk()``関数を使う場合**
コールバック関数の実行に成功した場合は``true``が返ります
内部関数を使うにはユーザー定義関数でラップすれば良い
配列を``直接``操作する

```php
array_walk($arr, 'callback'[, $third_argument]): bool
function callback($value, $index, $option)
{
	//
}
```

```php
<?php
$fruits = array("d" => "lemon", "a" => "orange", "b" => "banana", "c" => "apple");

function testAlter(&$item1, $key, $prefix)
{
    $item1 = $prefix . ":" . $item1;
}

function testPrint($item2, $key)
{
    echo $key . ". " . $item2 . "\n";
}

echo "Before ...:\n";
array_walk($fruits, 'testPrint');

array_walk($fruits, 'testAlter', 'fruit');
echo "... and after:\n";

array_walk($fruits, 'testPrint')
```
実行結果は以下のとおり

```php
Before ...:
d. lemon
a. orange
b. banana
c. apple
... and after:
d. fruit: lemon
a. fruit: orange
b. fruit: banana
c. fruit: apple
```

**``array_map()``を使う場合**
コールバック関数には``内部関数``とユーザー定義関数のどちらも使用可能
ただし**引数の数は配列の数と一緒**にする必要がある
また、**複数の配列を指定する場合は各配列の要素数が同じ**である必要がある
引数で渡した配列のキーが維持されるのは**渡した配列が一つ**だけであるときのみ
配列を``間接的に``操作するので**元の配列は保持される**

```php:
$new_arr = array_map($callback, $arr1[, $arr...]);
```

```php
<?php
function cube($n)
{
    return pow($n, 3);
}

$arr = [1, 2, 3, 4, 5];
$new_arr = array_map('cube', $arr);
print_r($new_arr);
```
実行結果は以下のとおりです

```
Array
(
    [0] => 1
    [1] => 8
    [2] => 27
    [3] => 64
    [4] => 125
)
```

```php
<?php
function show_Spanish($n, $m)
{
    return "The number " . $n . " is called " . $m . " in Spanish";
}

function map_Spanish($n, $m)
{
    return [$n => $m];
}

$a = [1, 2, 3, 4, 5];
$b = ['uno', 'dos', 'tres', 'cuatro', 'cinco'];

$c = array_map('show_Spanish', $a, $b);
print_r($c);

$d = array_map('map_Spanish', $a , $b);
print_r($d);
```
実行結果は以下のとおりです

```php
//$c
Array
(
    [0] => The number 1 is called uno in Spanish
    [1] => The number 2 is called dos in Spanish
    [2] => The number 3 is called tres in Spanish
    [3] => The number 4 is called cuatro in Spanish
    [4] => The number 5 is called cinco in Spanish
)

//$d
Array
(
    [0] => Array
        (
            [1] => uno
        )

    [1] => Array
        (
            [2] => dos
        )

    [2] => Array
        (
            [3] => tres
        )

    [3] => Array
        (
            [4] => cuatro
        )

    [4] => Array
        (
            [5] => cinco
        )

)
```
#連想配列を値で並べ替える
``asort()``関数、``arsort()``関数を使う
``asort()``関数は連想配列の``添え字と値の関連を維持したまま``、値を昇順に並び替える
``arsort()``関数は降順に並び替える
これらの関数は与えられた配列を``直接操作``する
第二引数には並び替えの比較方法を指定する
``SORT_NUMERIC``か``SORT_STRING``
返り値は``bool``

```php
<?php
$arr = ['a1' => 1, 'a2' => 2, 'a3' => 3];
arsort($arr, SORT_NUMERIC); //['a3' => 3, 'a2' => 2, 'a1' => 1]
```
#連想配列を添え字で並び替える
``ksort()``関数や``krsort()``関数を使う
上の``asort()``関数の添え字で並び替えるバージョンなので
上の説明を読み替えてください