我有一个List
Objects
(大约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秒 - 当然比我更舒服)并且想知道是否有更快的方法来实现这一点。
这段代码只会被使用一次所以不是真正的正常用法,通常我会因为这个原因而忽略它,但是为了学习目的而感到疑惑。
答案 0 :(得分: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
会将您的数据分组到以下群组中:
Key = 1: ID | Secondary_ID | Text | Number 1 | 1 | "something" | 3 1 | 1 | "something else"| 7 1 | 1 | "something1" | 4 Key = 2: ID | Secondary_ID | Text | Number 1 | 2 | "something2" | 344 Key = 3: ID | Secondary_ID | Text | Number 2 | 3 | "something3" | 74 2 | 3 | "something4" | 1
然后是.ToDictionary
方法:
x.Key
(我们分组的密钥,即Secondary_ID
)。String.Join
操作的结果作为值。正在加入的是该组内部元素的“文本:数字”集合 - x.Select(y => String.Format("{0} : {1}", y.Text, y.Number)
。答案 1 :(得分: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)时间它采取你的方法。
答案 2 :(得分: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;
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());
}
}