- カテゴリ: collection
- 掲載バージョン: Laravel 12・PHP 8.4
- 名前空間 / FQCN / コマンド:
Illuminate\Support\Collection::unique - 関連: uniqueStrict / duplicates / values / pluck / distinct(Query Builder)
- 変更履歴: 特筆なし
要点(TL;DR)
- 指定キー(またはコールバック)の値が同じ要素を先勝ちで1件に絞る
collect([...])->unique('email')- 罠:既定は緩やかな比較(==)/キーは保持/大量データはDB側の
distinct()を検討
概要
unique はコレクション内の重複を取り除きます。キー名やコールバックで「重複判定に使う値」を指定できます。既定は緩やかな比較(==)で、最初に現れた要素が残ります。Eloquentの大量行ではメモリ使用が増えるため、DBクエリでの distinct() や集計に切り替えるのが実務的です。
構文 / シグネチャ
// Collection
public function unique($key = null, bool $strict = false): static;
public function uniqueStrict($key = null): static;
- 引数(Collection::unique) 引数 型 必須 既定値 説明
$key`string callable null`$strictboolfalse厳密比較(===)にするか。trueなら型も区別 - 戻り値:
Collection(非破壊。先勝ちで要素採用/元キー保持) - 例外/副作用:ユーザー定義コールバックでの例外以外に特記なし
使用例
最小例
use Illuminate\Support\Collection;
$numbers = collect([1, '1', 2, 2, 3]);
$loose = $numbers->unique(); // 既定は緩やか: [1, 2, 3](キー保持)
$strict = $numbers->unique('', true); // 厳密: [1, '1', 2, 3]
// 連番にしたい場合
$reindexed = $loose->values(); // [1, 2, 3]
実務例:メールアドレスで重複ユーザーを除去
use App\Models\User;
use Illuminate\Support\Collection;
$users = User::query()
->select(['id','name','email'])
->latest('id')
->take(1000) // 例:直近1000件だけを見る
->get();
$uniqueByEmail = $users->unique('email'); // 重複メールは先に取得したユーザーのみ残す
foreach ($uniqueByEmail as $user) {
// 通知対象などに利用
}
ネストキー/コールバック
$data = collect([
['user' => ['id' => 10, 'name' => 'A']],
['user' => ['id' => 10, 'name' => 'A alt']],
['user' => ['id' => 11, 'name' => 'B']],
]);
// ドット記法
$byUserId = $data->unique('user.id');
// コールバック(メールを正規化して比較)
$records = collect([
['email' => 'Foo+tag@Example.com'],
['email' => 'foo@example.com'],
]);
$unique = $records->unique(fn ($row) => strtolower(preg_replace('/\+.*/', '', $row['email'])));
よくある落とし穴・注意
- 比較が既定で緩やか:
1と'1'は同一扱い。型も区別したいなら$strict=trueかuniqueStrict()。 - キーが保持される:
values()で整列しないと[0,1,2...]にならない。 - オブジェクト比較の期待外れ:オブジェクト同士の同一性ではなく、キーやコールバックで比較値を明示する。
- 大規模データのメモリ:アプリ側の
uniqueは全件を保持。DBでselect('email')->distinct()や集約、ストリーム処理(LazyCollection)を検討。 - 先勝ち:後から来た重複は捨てられる。必要なら事前に並び順を調整。
代替・関連APIとの比較
uniqueStrict($key = null):厳密比較(===)固定。型も区別。duplicates()/duplicatesStrict():重複している値の抽出に使う(原因調査向け)。array_unique()(PHP):配列ベースで近いが、コレクションのチェーンやコールバック/ドット記法は不可。- Query Builder
distinct():DB側で重複排除。件数が多い/I/O削減が目的ならこちらを優先。
テスト例(Pest)
<?php
use Illuminate\Support\Collection;
it('removes duplicates with loose compare and preserves keys', function () {
$c = collect([10 => 1, 11 => '1', 12 => 2]);
$u = $c->unique();
expect($u->keys()->all())->toBe([10, 12]) // 先勝ち・キー保持
->and($u->values()->all())->toBe([1, 2]);
});
it('can use strict compare', function () {
$c = collect([1, '1', 1]);
expect($c->unique('', true)->all())->toBe([1, '1']);
});
it('accepts dot notation and callback', function () {
$c = collect([
['user' => ['id' => 1]], ['user' => ['id' => 1]], ['user' => ['id' => 2]],
]);
expect($c->unique('user.id')->values()->count())->toBe(2);
$d = collect([['email' => 'A@ex.com'], ['email' => 'a@EX.com']]);
$u = $d->unique(fn($r) => strtolower($r['email']));
expect($u->count())->toBe(1);
});
トラブルシュート(エラー別)
| 症状/エラー | 原因 | 対処 |
|---|---|---|
| 重複が除去されない | 比較キー未指定でオブジェクトや配列をそのまま比較 | 'key' を指定、またはコールバックで比較値を返す |
1 と '1' が同一扱いになる | 既定が緩やかな比較 | $strict=true か uniqueStrict() を使う |
| 結果のキーが 0 始まりでない | 元キー保持仕様 | values() で再インデックス |
| メモリ使用量が多い | 全件をアプリ側に読み込み | DBの distinct() / 集約、chunk()、LazyCollection に切替 |
| 後から来た値を優先したい | 先勝ち仕様 | 事前に sortByDesc() などで並び替える |
参考リンク
- Laravel Docs — Collections:
unique/uniqueStrict - Laravel Docs — Collections:
duplicates/duplicatesStrict/values - Laravel Docs — Query Builder:
distinct() - PHP Manual —
array_unique

