のこのレイアウトに固執する必要がfoo
あり、実際に可能な限り高速にルックアップを行う必要がある場合(メモリサイズは気にせず、同じオブジェクトを繰り返し再利用するため、一連のセットアップのコストメモリ内の大きな構造はそれだけの価値があります)、それから私はします:
var byNameAndParentLookup = fooSource.ToLookup(f => Tuple.Create(f.parentid, f.name)); //will reuse this repeatedly
var results = byNameAndParentLookup[Tuple.Create(1, "bar2")].SelectMany(f => byNameAndParentLookup[Tuple.Create(f.id, "bar3")]);
とは言うものの、ツリーデータをメモリに保存する場合は、それぞれfoo
にchildren
コレクション(おそらく名前がキー設定された辞書)を持つツリー構造を作成することをお勧めします。
編集:少し説明します。
fooSource.ToLookup(f => Tuple.Create(f.parentid, f.name))
fooSource
(foo
オブジェクトがどこから来ているかに関係なく)のすべてのアイテムを調べ、それぞれに対してparentid
とのタプルを作成しname
ます。これはルックアップのキーとして使用されるため、親IDと名前の組み合わせごとに、その組み合わせで0個以上のfooオブジェクトを取得できます。(これはデフォルトの文字列比較を使用します。大文字と小文字を区別しないなど、他のものが必要な場合は、必要なIEqualityComparer<Tuple<int, string>>
比較を行う実装を作成して使用します.ToLookup(f => Tuple.Create(f.parentid, f.name), new MyTupleComparer())
)。
2行目は、次のように分類できます。
var partWayResults = byNameAndParentLookup[Tuple.Create(1, "bar2")];
var results = partWayResults.SelectMany(f => byNameAndParentLookup[Tuple.Create(f.id, "bar3")]);
最初の行は単にルックアップを検索するため、親IDが1で名前が「bar2」のfooオブジェクトの列挙を返します。
SelectMany
列挙型またはクエリ可能の各項目を取得し、列挙型を返す式を計算します。これは、単一の列挙型にフラット化されます。
言い換えれば、それはこのように少し動作します:
public static SelectMany<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, IEnumerable<TResult>> func)
{
foreach(TSource item in source)
foreach(TResult producedItem in func(item))
yield return producedItem;
}
この場合、渡された式は、最初のルックアップで見つかった要素のIDを取得し、それを親IDとして持ち、名前が「bar2」である要素を探します。
したがって、親IDが1で名前がbar2のすべてのアイテムについて、最初のアイテムのIDが親IDで名前がbar3のすべてのアイテムが見つかります。それが欲しかったのです。