こういうのやってる or やりたい人は結構いるんじゃないかと思ったんですが、意外にも検索してもいないので仕方なく記事を書きます。

環境


下記環境で確認してます。

  • PHP 7.2
  • PHP 7.3

やりたいこと


タイトルのとおりなのです。今回は下記のような多次元配列があったとして、キーがunkounchiの要素を変更もしくは削除したいという例でいきます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$data = [
'example' => 'example',
'unko' => 'bbbbb',
'test2' => [
'unko' => 'test2 unko',
'foo' => 'bar',
'unchi' => 'test2 unchi',
],
'test3' => [
'unko' => 'test3 unko',
'foo' => 'bar',
'test4' => [
'unko' => 'test4 unko',
'foo' => 'bar',
]
]
];

始めLaravelのcollectionあたりでなんとかできるんかな?と思ったのですが、できませんでした。配列が一次元になってもいいのであればflatMapで出来ると思います。

変更する場合の実装


変更するのであればarray_walk_recursiveとかいう長ったらしい関数で出来ます。キーがunkounchiの要素の値をtoilet!!に変更してみましょう。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
$data = [
'example' => 'example',
'unko' => 'bbbbb',
'test2' => [
'unko' => 'test2 unko',
'foo' => 'bar',
'unchi' => 'test2 unchi',
],
'test3' => [
'unko' => 'test3 unko',
'foo' => 'bar',
'test4' => [
'unko' => 'test4 unko',
'foo' => 'bar',
]
]
];
$deleteKeys = ['unchi', 'unko'];
array_walk_recursive($data, function (&$value, $key) use($deleteKeys) {
if (in_array($key, $deleteKeys)) {
$value = "toilet!!";
}
});

これで下記のような結果を得ることができます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[
'example' => 'example',
'unko' => 'toilet!!',
'test2' => [
'unko' => 'toilet!!',
'foo' => 'bar',
'unchi' => 'toilet!!',
],
'test3' => [
'unko' => 'toilet!!',
'foo' => 'bar',
'test4' => [
'unko' => 'toilet!!',
'foo' => 'bar',
]
]
]

削除する場合の実装


こちらはarray_walk_recursiveでできないので再帰で行うことにしました。適当に下記のような感じの関数を作ります。余談ですがもちろん下記のコードでも特定のキーの要素の値を変更することは可能です。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Example
{
/**
* 配列から指定したキーに一致する要素を削除します
*
* @param array 処理対象の配列
* @param array 削除したい要素のキーの配列
* @return array
*/
public static function deleteRecursively(array $array, array $keys): array
{
foreach($array as $k => $v) {
if (is_array($v)) {
$array[$k] = self::deleteRecursively($v, $keys);
} else {
if (in_array($k, $keys)) {
unset($array[$k]);
}
}
}
return $array;
}
}

引数に関数渡して実行するような実装にすればもっと柔軟にいろいろできるんじゃないかと思います。後はこいつに適当に渡せばOK

追記: 念のため書いておくと対象の配列の深さが深すぎるとスタックが積み重なって爆死すると思います。そもそもそのレベルの深さの配列だとそれ以前のところで問題が起こると思いますが…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
$data = [
'example' => 'example',
'unko' => 'bbbbb',
'test2' => [
'unko' => 'test2 unko',
'foo' => 'bar',
'unchi' => 'test2 unchi',
],
'test3' => [
'unko' => 'test3 unko',
'foo' => 'bar',
'test4' => [
'unko' => 'test4 unko',
'foo' => 'bar',
]
]
];

$deleteKeys = ['unchi', 'unko']; //消したい要素のキー
Example::deleteRecursively($data, $keys);

上記を実行すると下記のようなunkounchiが消えて綺麗になった結果が得られると思います。

1
2
3
4
5
6
7
8
9
10
11
12
[
'example' => 'example',
'test2' => [
'foo' => 'bar',
],
'test3' => [
'foo' => 'bar',
'test4' => [
'foo' => 'bar',
],
],
]

おわり。