- カテゴリ: collection
- 掲載バージョン: Laravel 12・PHP 8.4
- 名前空間 / FQCN / コマンド:
Illuminate\Support\Collection::diff - 関連: diffAssoc / diffKeys / intersect / unique / except
- 変更履歴: 特記事項なし
要点(TL;DR)
- 何に使うか:元のコレクションから、指定した配列/コレクションに存在しない値だけを取り出す。
- 最低限の使い方:
collect([1,2,3,4,5])->diff([2,4,6,8]); // [1,3,5] - よくある罠
- 値比較のみ(キーは無視)。キー差分は
diffKeysを使う - 比較はPHPの標準比較(型のゆるい等価
==に近い)で起きやすい型ゆらぎ - オブジェクトは「値等価」比較になりがち。IDで比較したい場合は
pluck('id')してから
- 値比較のみ(キーは無視)。キー差分は
概要
diff は「このコレクションにあって、相手側にはない値」を抽出します。値ベース比較のためキーは考慮されません。EloquentのID一覧やタグの除外など、セット演算の「差集合」を取りたい場面でよく使います。元のコレクションは変更されません(非破壊)。
構文 / シグネチャ
/**
* @param \Illuminate\Support\Collection|array $items
* @return \Illuminate\Support\Collection
*/
public function diff($items): static;
- 引数(表) 引数 型 必須 既定値 説明 $items array | Collection はい — 除外の基準となる値群。ここに含まれる「値」と一致したものを元コレクションから取り除く
- 戻り値:
Collection(元のキーは保持される) - 例外/副作用:例外は特になし。元コレクションは変更されない(非破壊)。
使用例
最小例
use Illuminate\Support\Collection;
$result = collect([1, 2, 3, 4, 5])->diff([2, 4, 6, 8]);
// $result: [0 => 1, 2 => 3, 4 => 5] // 元のキーが残る
実務例(Eloquent:割り当て済みIDとの差を取る)
use App\Models\Role;
use App\Models\User;
/** @var User $user */
$user = User::findOrFail(1);
// すでに付与済みのロールID
$assigned = $user->roles()->pluck('id'); // e.g. [2, 5, 7]
// 付与候補ID(フォーム入力など)
$candidates = collect([1, 2, 3, 4, 5, 6]);
// まだ付与されていないIDだけに絞る
$toAttach = $candidates->diff($assigned); // => [1,3,4,6]
$user->roles()->attach($toAttach->values()); // attachは連番キーが扱いやすいのでvalues()で詰め直す
よくある落とし穴・注意
- 値比較のみ:キーは無視されます。キーも含めて差分を取りたいなら
diffAssoc、キーだけ比較ならdiffKeys。 - 型ゆらぎ:比較はPHPの標準比較に準拠。
'10'と10が同一視されることがあります。厳密にしたい場合は事前にmap(fn($v) => (int)$v)などで型をそろえる。 - オブジェクト比較:オブジェクトは「値等価」比較(プロパティ構造が同じなら等しい)になり得ます。EloquentモデルはIDで比較するために
pluck('id')を用いるのが安全。 - パフォーマンス:概ね
O(n + m)目安。大規模データでは、比較側をflip()相当の集合に変換してfilterする実装のほうが速いケースがあります。 - LazyCollection:
LazyCollectionでも利用可能(非破壊・チェーン可)。
代替・関連APIとの比較
diff:値のみ比較。キーは無視。diffAssoc:キーと値をセットで比較(連想配列向け)。diffKeys:キーのみ比較。intersect:共通部分(積集合)。unique:自身の重複除去であり、他集合との比較ではない。
選定基準:
- 値だけ除外 ⇒
diff - キーも一致しているものを除外 ⇒
diffAssoc - キーリスト間の差だけ見たい ⇒
diffKeys - 共通要素を取りたい ⇒
intersect
テスト例(Pest)
<?php
use Illuminate\Support\Collection;
it('takes value-based difference and preserves original keys', function () {
$c = collect([10, 20, 30, 40]);
$r = $c->diff([20, 99, 40]);
expect($r->all())->toBe([0 => 10, 2 => 30]);
expect($c->all())->toBe([10, 20, 30, 40]); // 非破壊
});
it('be careful with loose comparisons', function () {
$r = collect([0, 1, 2, '10'])->diff(['0', 2, 10]);
// '10' と 10 が等価と判定される可能性に注意(事前に型揃え推奨)
expect($r->values()->all())->toBe([1]); // 型を統一していれば期待通りになる
});
トラブルシュート(エラー別)
| 症状/エラー | 原因 | 対処 |
|---|---|---|
| 期待した要素が残らない | 文字列と数値が等価判定されている | map で型を統一してから diff |
| 連想配列の比較で誤差分 | diff はキーを無視 | diffAssoc または diffKeys を使用 |
| モデルコレクションの差分が意図通りでない | オブジェクトの値等価比較になっている | pluck('id') でID配列にしてから比較 |
コレクション(カテゴリ)の補足
- チェーン可否:可
- 破壊的/非破壊:非破壊
- キー保持:保持(元コレクションのキーを温存)
- LazyCollection:対応
- 計算量の目安:概ね
O(n + m) - 入出力対応表(例)
| 入力(左:元 / 右:比較) | 出力 |
|---|---|
[1,2,3,4,5] / [2,4] | [1,3,5] |
['a'=>'x','b'=>'y'] / ['x'] | ['a'=>'x'](値比較、キーは残る) |
collect([1,'2',3]) / ['2'] | [0=>1, 2=>3](型ゆらぎに注意) |
参考リンク
- Laravel Docs(Collections
diff): https://laravel.com/docs/12.x/collections#method-diff - Laravel Docs(
diffAssoc): https://laravel.com/docs/12.x/collections#method-diffassoc - Laravel Docs(
diffKeys): https://laravel.com/docs/12.x/collections#method-diffkeys - PHP Manual(
array_diff): https://www.php.net/array_diff

