LINQのToDictionaryと似て非なるメソッドを作った。

LINQには、ToDictionaryというリスト型をディクショナリ型に変換する為のメソッドがある。
using System.Linq;

...
var numbers = new []{1, 2, 3, 4, 5, 6, 7};
var numberMap = numbers.ToDictionary(x => x);

任意の値をキーにして、該当する要素を取得したい時によく使う。ただし、このメソッドは、キーは重複できないというルールがあるようで、重複していると例外を発生させる。
 仕事では、この仕様が非常に不便だった。例えば、帳票等のデータを内部に持つとき、ヘッダ:明細=1:Nで設計することが多い。というより、これが普通なんだ。複数の帳票(ヘッダ)に関する明細を、DBのテーブルから一気に(1クエリで)検索し、ヘッダ毎にグルーピングしたい時に、このメソッドが使えなくて、悶えてしまった。
結局は、独自にメソッドを作ってそれで凌ぐ事になった。
using System;
using System.Collections.Generic;

namespace TawamureDays {

public static class Utils {

/// <summary>
/// 指定のリストから、キーセレクタ(keySelector)を使って、ディクショナリを構築します。
/// </summary>
/// <typeparam name="TKey">キーの型宣言</typeparam>
/// <typeparam name="TItem">アイテムの型宣言</typeparam>
/// <param name="itemList">リスト</param>
/// <param name="keySelector">キーセレクタ(キーを取得する)</param>
/// <returns>ディクショナリ</returns>
public static IDictionary<TKey, IList<TItem>> ToDictionary<TKey, TItem>(
IEnumerable<TItem> itemList, Func<TItem, TKey> keySelector) {
if (keySelector == null) {
throw new ArgumentNullException("keySelector");
}

var dictionary = new Dictionary<TKey, IList<TItem>>();

if (!Utils.IsNullOrEmpty(itemList)) {

foreach (TItem item in itemList) {
var key = keySelector(item);

if (!dictionary.ContainsKey(key)) {
dictionary[key] = new List<TItem>();
}

dictionary[key].Add(item);
}
}

return dictionary;
}
}
}
ディクショナリのValueにあたる部分がIList>TItem>型になっているだけ。
重複してもリストに追加されていく。
using System;
using System.Windows;

using TawamureDays;

var list = new []{1, 2, 3, 4, 5, 5, 7, 8, 9, 10};
//例外は発生しない
var dict = Utils.ToDictionary(list, x => x);

//使い方:キーが「5」のリストを取得する。
IList<int> partialList = null;

if (dict.TryGetValue(5, out partialList) {
foreach (var n in partialList) {
//...
}
}

で、よく考えると重複しているキーをあぶりだすのにも使えたりするかも。
var list = new []{1, 2, 3, 4, 5, 5, 7, 8, 9, 10, 11, 1, 2};
var dict = Utils.ToDictionary(list, x => x);
//重複しているキーを抽出する。
var duplicated = dict.Where(item => item.Value.Count > 1).
Select(item => item.Key).
ToArray();

その他メモ:
・Dictionary<TKey, TItem>は、存在しないキーを指定して取得しようとすると、KeyNotFoundExceptionが発生するので、ContainsKeyメソッドで確認するか、TryGetValueメソッドで例外が発生しないメソッドを使っている。

・キーが存在するかしないかを確認するだけなら、List<T>よりHashSet<T>の方が速い。
C#, List.Contains() - too slow?

・Dictionary<TKey, TItem>をforeachで回すと、要素は、KeyValuePair<TKey, TItem>型になる。
・Dictionary<TKey, TItem>の複数スレッド処理対応版として、ConcurrentDictionary<TKey, TItem>がある。
スポンサーサイト
当サイトは基本をすっ飛ばしてます。基本文法等は、@ITをどうぞ
カテゴリー: C# | コメント: 0 | トラックバック: 0


この記事へのコメント

コメントの投稿

非公開コメント


サイドバー背後固定表示サンプル

当ブログに書かれたソースコードは流用自由です。

バグ、スペルミス等はありうる事です。

ご利用の際は自己責任でお願いしますm(_ _)m