1

我有一个ListObjects大约 100k)必须迭代才能生成Dictionary. 但是代码执行得很慢,特别是在一行上

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这首先通过using分组GroupBy,然后使用 将结果放入字典中ToDictionary

GroupBy会将您的数据分为以下几组:

键 = 1:

身份证 | 次要ID | 正文 | 数字
1 | 1 | “东西” | 3
1 | 1 | “别的东西”| 7
1 | 1 | “东西1” | 4

键 = 2:
身份证 | 次要ID | 正文 | 数字
1 | 2 | “东西2” | 344

键 = 3:
身份证 | 次要ID | 正文 | 数字
2 | 3 | “东西3” | 74
2 | 3 | “东西4” | 1

然后.ToDictionary方法:

  • 选择键为x.Key(我们分组的键,即Secondary_ID)。
  • 选择String.Join操作的结果作为值。正在加入的是来自该组内元素的“文本:数字”的集合 - 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(1) 时间内)找到任何给定项目所属的存储桶,而不是 O(n) 时间采取你的方法。

于 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 不能包含重复键,所以无论你在这里使用ID还是Secondary_ID,如果你Secondary_ID必须在现有的范围内ID;你甚至不需要Distinct()在代码中。

通过做一些简化,原始代码将是:

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 回答