配列の中に重複した値があると、処理が複雑になったりバグの原因になることがあります。特にキーを維持したまま重複を削除したい場合、ただ単に値を削るだけでは意図しない結果になることがあります。この記事では「PHP 配列 重複 削除 キー保持」をテーマに、最新情報を整理しつつ、使える関数や手法を豊富な実例とともに解説します。インデックスを維持したまま重複のない配列を作る方法をしっかり理解できます。
PHP 配列 重複 削除 キー保持 の基本概念
PHPで配列から重複した値を削除しつつ、**キーを保持**するとはどういう意味でしょうか。PHPの配列はインデックス配列と連想配列の両方を扱え、デフォルトのキー(数値キー)や文字列キーが含まれます。重複とは「同じ値が複数回出現すること」です。キー保持とは、「最初に現れた要素のキーはそのまま残す」「先頭以外の重複要素は削除する」が基本ルールです。
PHPには標準関数としてこの機能を持つものがあります。また多次元配列や配列の中のオブジェクトには標準関数だけでは不十分なケースがあります。そうした場合の代替手法についても知っておく必要があります。これにより、意図せずキーを詰められたり、インデックス順序が崩れるといった問題を回避できます。
array_unique関数によるキー保持の動作
PHPの標準関数である array_unique は、配列から重複した値を削除し、新しい配列を返します。この関数は、**最初に現れた同じ値の要素について、そのキーと値を保持**し、後続の重複要素は削除されます。これはインデックス配列でも連想配列でも同じ動作です。数値と文字列の比較方法を制御するためのオプションフラグもあります。
例えば、SORT_REGULAR や SORT_STRING といったフラグを指定することで、型を考慮する比較や文字列として比較するなどの挙動を調整できます。これにより「10」と 10 を同じとみなすかどうかなどを制御でき、キー保持を行いつつ重複の定義を柔軟にできます。
キーが詰められるケースとその防ぎ方
array_unique を使ったあとは、配列のキーがそのまま残りますが、連番インデックス(0,1,2…)を期待する場面ではキーが「穴あき」になることがあります。こうした場合には array_values を使ってキーを詰めて再インデックス化する手法が一般的です。ただしこの操作はキーそのものを保持する意味を失うため、文字列キーや特定のインデックスを利用している場合は注意が必要です。
標準関数の限界と注意点
array_uniqueは一次元配列に対しては強力ですが、多次元配列や配列の値が配列/オブジェクトである場合、「値の文字列化」による比較が行われます。この場合、配列を文字列化すると常に同じ文字列「Array」になるため、期待通りに重複が判断されないことがあります。また、enum 型オブジェクトなど、文字列表現を持たない値に対しては利用できないことがあります。
実践:重複削除しキー保持する具体的手法とコード例
ここでは「PHP 配列 重複 削除 キー保持」を実際に実現する方法をコード例とともに示します。用途別に使えるテクニックを整理しており、最新のPHPバージョンで動くコードを書いています。
一次元配列で array_unique を使う方法
もっともシンプルなケースとして、一次元配列(数値インデックスまたは文字列キー)から重複を削除しつつキーを維持したい場合の基本的なコード例です。
例:
<?php
$array = [0=>'apple', 2=>'banana', 5=>'apple', 7=>'cherry', 9=>'banana'];
$unique = array_unique($array, SORT_STRING);
// 結果:キー 0 の apple、キー 2 の banana、キー 7 の cherry が残る
この手法のポイント:
- キーを保持しつつ、最初に現れた重複の要素を残す。
- 型比較が必要であれば SORT_REGULAR, SORT_NUMERIC, SORT_STRING などを適切に使う。
- その後キーを振り直したければ array_values を使うが、その際は “キー保持” の意図を確認する。
多次元配列で特定フィールド重複を削除しキー保持する方法
ユーザー情報やデータ行など、配列の各要素が配列やオブジェクトである多次元配列の場合、一つのキー(例えば id や name)で重複を判断しつつ、その行全体を残す方法があります。
例:
<?php
$records = [
0 => ['id'=>1, 'name'=>'A'],
1 => ['id'=>2, 'name'=>'B'],
3 => ['id'=>1, 'name'=>'A2'],
5 => ['id'=>3, 'name'=>'C'],
];
$seen = [];
$result = [];
foreach ($records as $key => $row) {
if (!isset($seen[$row['id']])) {
$seen[$row['id']] = true;
$result[$key] = $row;
}
}
// 結果:キー 0,1,5 の要素が残る
この方法の利点と注意点:
- キーそのもの(0,1,3,5など)をそのまま保持できる。
- 重複判断に使うフィールドを柔軟に指定できる。
- ループ処理が入るため、巨大配列ではパフォーマンスに注意が必要。
多次元配列全体で値そのものを比較して重複削除する方法
もし配列要素が配列のような構造を持ち、それら全体が同一かどうかで重複を判断したい場合、値全体を文字列化して unique 処理を行う方法があります。キー保持と元のインデックスの維持を視野に入れた実装です。
例:
<?php
$items = [
10 => ['a'=>1,'b'=>2],
12 => ['a'=>3,'b'=>4],
14 => ['a'=>1,'b'=>2],
];
$serialized = array_map('serialize', $items);
$unique_serialized = array_unique($serialized);
$result = array_intersect_key($items, $unique_serialized);
// 結果:キー 10 と 12 の要素が残る
この方法が使える場面:
- 多次元配列の要素全体が重複対象である場合。
- 標準関数のみで比較処理を簡潔に書きたいとき。
- ただし serialize/unserialize によるコストや値の同型性に注意。
パフォーマンスと実用性の比較
キー保持しながら重複削除を行う手法には複数あり、使いどころによってベストな方法が異なります。ここでは代表的な手法を比較表で示します。最新の環境での挙動をもとにしています。
| 手法 | キー保持 | 重複判断の対象 | 簡潔さ | パフォーマンスの目安 |
|---|---|---|---|---|
| array_unique(一次元) | 最初のキーを保持 | 値そのもの | 非常に簡単 | 高速(内部最適化済み) |
| ループ + seen配列(多次元・特定キー) | 元のキーをそのまま維持可能 | 指定フィールド | コードは少し長い | 中規模/大規模でも許容範囲 |
| serialize + array_unique + array_intersect_key | 元のキー保持可能 | 要素全体(全フィールド) | 中程度 | シリアライズのコストあり、大規模配列では注意 |
最新の PHP バージョンでの更新点と注意事項
PHP のバージョンが進むにつれて、enum やオブジェクトの扱いなど、重複削除に関連して新たな注意点が増えてきています。標準関数が扱えないケースや型の比較の仕様変更に注目しておきましょう。
enum 型の扱い
enum(特に BackedEnum)など、文字列表現を持たない型の場合、array_unique に直接渡すとエラーが出ることがあります。値の比較に使う際は、enum のプロパティや name/value を抜き出して比較用のキーを生成する方法が推奨されます。
オブジェクトや配列の比較方法の注意点
オブジェクトや多次元配列を array_unique に渡した場合、内部で文字列化されます。配列なら serialize→unserialize で比較する方法、オブジェクトなら toString や明示的な比較関数を使うほうが安全です。特定フィールドのみ比較したい場合は、配列_column やカスタム比較関数を組み合わせた手法が効果的です。
メモリ・処理速度に関する考慮
非常に大きな配列(何万件~数百万件)に対しては、シリアル化や in_array を多用する方法はメモリ使用率が増加し、処理時間も長くなります。可能であれば基本的な array_unique を使い、必要な場合のみループ+判定ロジックを導入するのが実用的です。最新の環境では性能改善が図られているものの、基本設計は変わっていません。
応用例と実践的ユースケース
実際の開発現場では、ただ値を独自に削除するだけでなく、API レスポンスの整形やデータベースデータの前処理といった場面で「キー保持での重複削除」が役立ちます。ここでは応用例を2つ紹介します。
API レスポンスで冗長なデータを排除する
外部 API から取得したデータが重複するフィールドを含むとき、重複する行を削除してクリーンにするために、特定フィールドをキーとして判定しつつ重複を削除する方法が有効です。ここでは identifier フィールドで重複チェックし、キーを保持することでログの解析やデバックがしやすくなります。
具体的には、API から取得した配列に対して先ほどのループ+ seen 配列によるフィルタリングを使い、結果をキー付きで返すことで、「何番目の要素だったか」が追える状態を保てます。
データベースからフェッチした多次元配列の整理
複数テーブルを JOIN して得た結果配列に、同じキーの行が複数存在するケースがあります。例えばユーザーごとに複数のログエントリーがあるが、ユーザーごとに重複を削除したい場合など。ここでも多次元配列の特定フィールド(例 user_id)を重複判定に使い、元のインデックスもキーとして維持することで、その後の加工処理で行番号や元データとの対応が取れるようになります。
まとめ
「PHP 配列 重複 削除 キー保持」を実現するには、まず一次元配列なら標準関数 array_unique を使って最初の要素のキーをそのまま残すことが基本です。インデックス配列でも連想配列でも動作します。
多次元配列や特定フィールドでの重複削除が必要な場合は、ループと見た目に整った判定ロジックを組むことで、キー保持しつつ重複を除去できます。serialize+比較、array_column+array_intersect_key 等の手法も選択肢に入ります。
enum やオブジェクトの扱い、巨大配列でのパフォーマンス、キーの再インデックス化の有無など、注意するポイントはいくつかありますが、目的と条件に応じて適切な手法を選べば、重複削除とキー保持は十分に可能です。開発現場でのデータ整理やAPI設計にぜひ活かして下さい。
コメント