Skip to content

DistinctUntilChanged

1. 概要

DistinctUntilChanged は、直前の要素と同じ値が連続する場合にその重複を排除するフィルタリングオペレーターです。値が実際に変化したときだけ要素を通過させます。UI のプロパティ変更通知やセンサーデータの変化検出など、連続する同一値を無視したいケースで広く使われます。

2. シグネチャ

デフォルト比較による連続重複排除

csharp
public static Observable<T> DistinctUntilChanged<T>(
    this Observable<T> source)

デフォルトの等値比較(EqualityComparer<T>.Default)を使用して、直前の要素と比較します。

csharp
// 連続する同一値を排除
source.DistinctUntilChanged();

カスタム比較子による連続重複排除

csharp
public static Observable<T> DistinctUntilChanged<T>(
    this Observable<T> source,
    IEqualityComparer<T> comparer)

指定した IEqualityComparer<T> を使用して直前の要素と比較します。

csharp
// 大文字小文字を無視して連続重複を排除
source.DistinctUntilChanged(StringComparer.OrdinalIgnoreCase);

overload の使い分け

overload使う場面
引数なしデフォルトの等値比較で十分な場合
IEqualityComparer<T>カスタムの比較ロジックが必要な場合

3. マーブルダイアグラム

DistinctUntilChanged のマーブルダイアグラム

直前の要素と異なる値が来たときだけ下流に流れます。同じ値が連続する場合、最初の 1 つだけが通過し、後続の同一値は破棄されます。ただし、間に異なる値が挟まれば同じ値でも再度通過します。

4. サンプルコード

csharp
using R3;

// 連続する重複値の排除
var values = new[] { 1, 1, 2, 2, 2, 3, 1, 1, 3 };

values.ToObservable()
    .DistinctUntilChanged()
    .Subscribe(x => Console.WriteLine($"値: {x}"));
// 出力:
// 値: 1
// 値: 2
// 値: 3
// 値: 1  ← 間に 3 が挟まったので再度通過
// 値: 3

// UI プロパティの変更検出(ReactiveProperty との組み合わせ例)
Observable.EveryValueChanged(this, x => x.IsEnabled)
    .DistinctUntilChanged()
    .Subscribe(enabled => Console.WriteLine($"IsEnabled 変更: {enabled}"));

5. 補足

Distinct との違い

  • Distinct: シーケンス全体で一意性を保証します。一度出現した値は二度と通過しません。
  • DistinctUntilChanged: 直前の要素との比較のみ行います。間に別の値が挟まれば、同じ値でも再度通過します。

メモリ消費の観点では、DistinctUntilChanged は直前の値 1 つだけを保持するため一定ですが、Distinct は出現したすべての値を HashSet に保持します。

DistinctUntilChangedBy との違い

要素そのものではなく、特定のプロパティ(キー)に基づいて連続重複を判定したい場合は DistinctUntilChangedBy を使用してください。

csharp
// 要素全体で比較
source.DistinctUntilChanged();

// 特定のプロパティで比較
source.DistinctUntilChangedBy(x => x.Category);