1

を生成するために反復する必要があるListof Objects(約 100k) がありDictionaryます。ただし、コードは非常に遅く、特に 1 行で実行されます

public class Item{
        public int ID;
        public int Secondary_ID;
        public string Text;
        public int Number;
}

データは次のようになります (100k 行)

ID  | Secondary_ID |      Text       | Number
1   |    1         | "something"     | 3
1   |    1         | "something else"| 7
1   |    1         | "something1"    | 4
1   |    2         | "something2"    | 344
2   |    3         | "something3"    | 74
2   |    3         | "something4"    | 1

完成したらこんな感じにしたいです。(正直なところ、どんなコレクションでも構いません)

 Dictionary<int, string> 

Key             | Value
(secondary_ID)  | (Text : Number)

1               | "Something : 3, Something else : 7, Something1 : 4"
2               | "Something2 : 344"
3               | "Something3 : 74, Something4 : 1"

私のコードは現在、このように動作し、ListAllすべてのデータが含まれています。

var Final=new Dictionary<int, string>();
var id1s=ListAll.Select(x => x.ID).Distinct().ToList();

foreach(var id1 in id1s) {
    var shortList=ListAll.Where(x => x.ID==id1).ToList(); //99% of time spent is here
    var id2s=shortList.Select(x => x.Secondary_ID).Distinct().ToList();

    foreach(var id2 in id2s) {
        var s=new StringBuilder();
        var items=shortList.Where(x => x.Secondary_ID==id2).ToList();

        foreach(var i in items) {
            s.Append(String.Format("{0} : {1}", i.Text, i.Number));
        }

        Final.Add(id2, s.ToString());
    }
}

return Final;

現在、出力は正しいですが、上記のコメントに記載されているように、これは処理に非常に長い時間がかかり (90 秒 - 確かに私が満足しているよりも長く)、これを達成するためのより速い方法があるかどうか疑問に思っていました。

このコードは一度しか使用されないため、実際には通常の使用法ではなく、通常はその理由で無視しますが、学習目的で疑問に思っていました.

4

3 に答える 3

8

これが私がすることです(テストされていませんが、うまくいけばアイデアが得られます):

var final = ListAll.GroupBy(x => x.Secondary_ID)
                   .ToDictionary(x => x.Key, x => String.Join(", ", 
                       x.Select(y => String.Format("{0} : {1}", 
                           y.Text, y.Number)))

Secondary_IDこれは最初にを使用してグループ化しGroupBy、次に を使用して結果を辞書に入れToDictionaryます。

GroupBy、データを次のグループにグループ化します。

キー = 1:

ID | セカンダリ ID | テキスト | 番号
1 | 1 | "何か" | 3
1 | 1 | "他の何か"| 7
1 | 1 | "何か1" | 4

キー = 2:
ID | セカンダリ ID | テキスト | 番号
1 | 2 | "何か2" | 344

キー = 3:
ID | セカンダリ ID | テキスト | 番号
2 | 3 | "何か3" | 74
2 | 3 | "何か4" | 1

次に.ToDictionary方法:

  • x.Keyキーを(グループ化したキー、つまり)として選択しますSecondary_ID
  • 演算の結果をString.Join値として選択します。結合されているのは、そのグループ内の要素からの "Text : Number" のコレクションですx.Select(y => String.Format("{0} : {1}", y.Text, y.Number)
于 2013-01-08T17:31:40.573 に答える
7

アイテムを ID でグループ化する、はるかに効率的な (さらに書きやすい) 方法は、 を使用することGroupByです。

var query = ListAll.GroupBy(x => x.Secondary_ID)
    .ToDictionary(group => group.Key,
        group => string.Join(", ",
             group.Select(item => string.Format("{0} : {1}",item.Text , item.Number))),
    //consider refactoring part of this line out to another method
    });

コードが非常に遅い理由として、リスト全体を個別の ID ごとに検索しています。これは O(n^2) 操作です。 GroupByそれはしません。グループ化するものに基づいて内部的にハッシュベースの構造を使用するため、O(n) 時間ではなく、特定のアイテムが属するバケットをすばやく (O(1) 時間で) 見つけることができます。あなたの方法を取ります。

于 2013-01-08T17:32:15.143 に答える
0

まず、どこでも削除するToList()と、速くなるはずです。熱心な評価ToList()を行いますので、

あなたのコードが期待していることは次のとおりだと思います:

var Final=new Dictionary<int, string>();

foreach(var x in ListAll)
    if(Final.ContainsKey(x.Secondary_ID))
        Final[x.Secondary_ID]+=String.Format(", {0} : {1}", x.Text, x.Number);
    else
        Final.Add(x.Secondary_ID, String.Format("{0} : {1}", x.Text, x.Number));

return Final;

A Dictionary に重複するキーを含めることはできないため、ここでIDorありません。また、コード内では必要ありませんSecondary_IDSecondary_IDIDDistinct()

単純化すると、元のコードは次のようになります。

foreach(var id1 in ListAll.Select(x => x.ID).Distinct()) {
    foreach(var id2 in ListAll.Where(x => x.ID==id1).Select(x => x.Secondary_ID).Distinct()) {
        var s=new StringBuilder();

        foreach(var i in ListAll.Where(x => x.ID==id1).Where(x => x.Secondary_ID==id2)) {
            s.Append(String.Format("{0} : {1}", i.Text, i.Number));
        }

        Final.Add(id2, s.ToString());
    }
}
于 2013-01-09T00:21:14.030 に答える