1

私はかなり大きなコレクションを持っていますfoo { int id, int parentid, string name}

fooオブジェクトの名前が「bar3」で、IDが。のオブジェクトの子である「bar2」という名前のオブジェクトの子であるオブジェクトのリストを収集しようとしています1

どのような種類のコレクションを使用する必要がありますか(私はルックアップと辞書で遊んでいますが、あまり成功していません)、これを効率的に機能させるためにこれをどのように書く必要がありますか?約30Kfooのオブジェクトがあり、私の方法は窒息死します。

ありがとう!

4

3 に答える 3

3

のこのレイアウトに固執する必要が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")]);

とは言うものの、ツリーデータをメモリに保存する場合は、それぞれfoochildrenコレクション(おそらく名前がキー設定された辞書)を持つツリー構造を作成することをお勧めします。

編集:少し説明します。

fooSource.ToLookup(f => Tuple.Create(f.parentid, f.name))

fooSourcefooオブジェクトがどこから来ているかに関係なく)のすべてのアイテムを調べ、それぞれに対して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のすべてのアイテムが見つかります。それが欲しかったのです。

于 2012-08-08T13:46:30.850 に答える
0

これをチェックしてください:QuickGraph 私は実際にそれを使ったことがありませんが、それは十分に文書化されているようです。または、 C5 GenericCollectionLibraryを試すこともできます

私はこのトレッドからこれを手に入れました

于 2012-08-08T13:33:57.437 に答える
-1

最初にすべてのアイテムをparentIdでグループ化し、次に条件を適用することをお勧めします。最初に、bar1要素を持つグループを見つける必要があります。それよりも、そのすべての子を選択して、bar2という名前の要素を見つけようとする必要があります...

私はそのような解決策を提案することができます、それは最善ではありませんが、それは機能します(thirdLevelElementsには必要な要素が含まれます)。明確にするためにforeachsを使用しました。このロジックは、linqステートメントで記述できますが、私にとっては理解が複雑になります。

var items = new[]
                            {
                                new Foo{id=1,parentid = 0, name="bar1"},
                                new Foo{id=2,parentid = 1, name="bar2"},
                                new Foo{id=3,parentid = 2, name="bar3"},
                                new Foo{id=4,parentid = 0, name="bar12"},
                                new Foo{id=5,parentid = 1, name="bar13"},
                                new Foo{id=6,parentid = 2, name="bar14"},
                                new Foo{id=7,parentid = 2, name="bar3"}
                            };

            var groups = items.GroupBy(item => item.parentid).ToList();
            var firstLevelElements = items.Where(item => item.name == "bar1");
            List<Foo> secondLevelElements = new List<Foo>();
            foreach (var firstLevelElement in firstLevelElements)
            {
                secondLevelElements.AddRange(groups[firstLevelElement.id]
                    .Where(item => item.name == "bar2"));
            }
            List<Foo> thirdLevelElements = new List<Foo>();
            foreach (var secondLevelElement in secondLevelElements)
            {
                thirdLevelElements.AddRange(groups[secondLevelElement.id]
                    .Where(item => item.name == "bar3"));
            }
于 2012-08-08T13:19:34.517 に答える