diff — コレクションの差分(値ベース)

collection
  • カテゴリ: 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 する実装のほうが速いケースがあります。
  • LazyCollectionLazyCollection でも利用可能(非破壊・チェーン可)。

代替・関連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](型ゆらぎに注意)

参考リンク

長野県・北アルプス地方在住のフリーランスWebプログラマー。
「落ち着くためのWeb開発」をテーマに、訪れる人が安心して使えるサービスづくりを心がけています。

LaravelやWordPressなどのWebアプリケーション開発を得意とし、技術面の安定性はもちろん、運用後も長く活用できる設計を大切にしています。
静かな山間の暮らしから生まれる視点で、シンプルかつ本質的な解決策をご提案します。

野鳥観察も趣味のひとつで、特にミソサザイ(Wren)に魅力を感じています。
小さな体に反して力強く上向きの尾羽、そして澄んだ鳴き声が遠くまで響く姿に、静かな存在感と芯の強さを感じます。
このサイト名「Laravel Wren」には、そんなミソサザイのように、小さくても確かな価値を届けたいという想いを込めています。

信頼できるパートナーとして、そして気軽に相談できる存在として、あなたのWebプロジェクトをサポートします。

Yudai Tsuyuzakiをフォローする