Home / ぼやきごと / 2013-03-16
2013-03-16

C#: SortedList や SortedDictionary における特定値未満・以下・以上・超のキーを持つ要素の取得

C#の SortedList<TKey, TValue> ジェネリッククラスや SortedDictionary<TKey, TValue> ジェネリッククラスには、C++の std::map テンプレートクラスにあるメンバ関数 lower_boundupper_bound に相当するメソッドがありません。
ではどうするのかというと、 .NET Framework 3.5 & C# 3.0 以降では IEnumerable<T> インタフェースに対する拡張メソッドである FirstOrDefault および LastOrDefault を使います。

すべて開くすべて閉じる
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
-
!
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
!
 
 
 
 
 
 
 
-
-
!
|
|
|
|
|
|
|
|
|
|
|
|
|
!
// using System.Linq; が必要です。
 
/// <summary>
/// 指定したキーの前後にある要素を取得する。
/// </summary>
/// <typeparam name="TKey">キーの型。</typeparam>
/// <typeparam name="TValue">値の型。</typeparam>
/// <param name="sortedList">キーでソートされたリスト。</param>
/// <param name="key">検索対象のキー。</param>
/// <param name="lessThan">key より小さいキーを持つ直近の要素。</param>
/// <param name="lessEqual">key 以下のキーを持つ直近の要素。</param>
/// <param name="greaterEqual">key 以上のキーを持つ直近の要素。</param>
/// <param name="greaterThan">key より大きいキーを持つ直近の要素。</param>
/// <param name="comparer">
/// 比較子。既定の比較子を用いるならば null 。
/// </param>
/// <remarks>
/// いずれの out 引数も、要素が見つからなければ
/// default(KeyValuePair{TKey, TValue}) が設定される。
/// </remarks>
void GetBoundKeys<TKey, TValue>(
    IEnumerable<KeyValuePair<TKey, TValue>> sortedList,
    TKey key,
    out KeyValuePair<TKey, TValue> lessThan,
    out KeyValuePair<TKey, TValue> lessEqual,
    out KeyValuePair<TKey, TValue> greaterEqual,
    out KeyValuePair<TKey, TValue> greaterThan,
    IComparer<TKey> comparer = null)
{
    // comparer が null なら既定の比較子を用いる
    var comp = comparer ?? Comparer<TKey>.Default;
 
    lessThan =
        sortedList.LastOrDefault(
            kv => comp.Compare(kv.Key, key) < 0);
    lessEqual =
        sortedList.LastOrDefault(
            kv => comp.Compare(kv.Key, key) <= 0);
    greaterEqual =
        sortedList.FirstOrDefault(
            kv => comp.Compare(kv.Key, key) >= 0);
    greaterThan =
        sortedList.FirstOrDefault(
            kv => comp.Compare(kv.Key, key) > 0);
}

拡張メソッド FirstOrDefault は、条件に合致する(=引数のデリゲートが true を返す)最初の要素を返します。
拡張メソッド LastOrDefault は、条件に合致する(=引数のデリゲートが true を返す)最後の要素を返します。
いずれのメソッドも見つからなかった場合は要素型の既定値(上述のコードならば default(KeyValuePair<TKey, TValue>))を返します。

要素が存在するとわかっている場合には拡張メソッド First および Last も使えます。
こちらはリストが空である場合や要素が見つからなかった場合に例外をスローします。

なお、どのメソッドにもデリゲートを引数に取らないオーバロードがあり、そちらは単純にリスト内で一番最初および一番最後の要素を返します。

追記
当然ながらこれらのメソッドは対象のリストがソート済みであることを考慮してはくれません。
二分探索法などを駆使したい場合は自前でコーディングする必要があるでしょう。
Category: [プログラミング][C#] - 2013-03-16 18:10:21