给定一个有序的字符串集合:
var strings = new string[] { "abc", "def", "def", "ghi", "ghi", "ghi", "klm" };
使用LINQ创建字符串字典到集合中该字符串的出现次数:
IDictionary<string,int> stringToNumOccurrences = ...;
最好在琴弦集合中一次性完成此操作......
答案 0 :(得分:8)
var dico = strings.GroupBy(x => x).ToDictionary(x => x.Key, x => x.Count());
答案 1 :(得分:5)
Timwi / Darin的建议将在原始集合的一次传递中执行此操作,但将为分组创建多个缓冲区。 LINQ并不是非常擅长这种计数,这样的问题是我编写Push LINQ的最初动机。您可能希望阅读我的blog post,了解有关为什么LINQ在这里效率不高的详细信息。
推LINQ和相同想法的更令人印象深刻的实现 - Reactive Extensions - 可以更有效地处理这个问题。
当然,如果您不太关心额外的效率,请使用GroupBy
答案:)
public static IDictionary<string, int> CountEntries(IEnumerable<string> strings)
{
var dictionary = new Dictionary<string, int>();
using (var iterator = strings.GetEnumerator())
{
if (!iterator.MoveNext())
{
// No entries
return dictionary;
}
string current = iterator.Current;
int currentCount = 1;
while (iterator.MoveNext())
{
string next = iterator.Current;
if (next == current)
{
currentCount++;
}
else
{
dictionary[current] = currentCount;
current = next;
currentCount = 1;
}
}
// Write out the trailing result
dictionary[current] = currentCount;
}
return dictionary;
}
这是O(n),除了写入值时,还包括 no 字典查找。另一种实现方式是使用foreach
和current
值从null开始...但最终在其他几种方式上变得非常狡猾。 (我已经尝试过了:)当我需要第一个值的特殊情况处理时,我通常会使用上面的模式。
实际上你可以使用Aggregate
使用LINQ执行此操作,但这会非常讨厌。
答案 2 :(得分:3)
标准的LINQ方式是:
stringToNumOccurrences = strings.GroupBy(s => s)
.ToDictionary(g => g.Key, g => g.Count());
答案 3 :(得分:0)
如果这是实际的生产代码,我会选择Timwi's response。
如果这确实是家庭作业,并且你应该编写自己的实现,那就不应该太难了。以下是一些指示您指向正确方向的提示:
Dictionary<TKey, TValue>
有ContainsKey
方法。IDictionary<TKey, TValue>
接口的this[TKey]
属性是可设置的;即,你可以dictionary[key] = 1
(这意味着你也可以dictionary[key] += 1
)。从这些线索中我认为你应该能够“手工”弄清楚如何做到这一点。
答案 4 :(得分:0)
如果您正在寻找特别高效(快速)解决方案,那么GroupBy
对您来说可能太慢了。你可以使用一个循环:
var strings = new string[] { "abc", "def", "def", "ghi", "ghi", "ghi", "klm" };
var stringToNumOccurrences = new Dictionary<string, int>();
foreach (var str in strings)
{
if (stringToNumOccurrences.ContainsKey(str))
stringToNumOccurrences[str]++;
else
stringToNumOccurrences[str] = 1;
}
return stringToNumOccurrences;
答案 5 :(得分:0)
这是一个 foreach 版本,就像Jon提到的那样,他在答案中发现“相当狡猾”。我把它放在这里,所以有一些具体可谈的内容。
我必须承认,我发现它比Jon的版本更简单,并且无法真正看到它的含义。乔恩?任何人吗?
static Dictionary<string, int> CountOrderedSequence(IEnumerable<string> source)
{
var result = new Dictionary<string, int>();
string prev = null;
int count = 0;
foreach (var s in source)
{
if (prev != s && count > 0)
{
result.Add(prev, count);
count = 0;
}
prev = s;
++count;
}
if (count > 0)
{
result.Add(prev, count);
}
return result;
}
更新为空源添加必要的检查 - 我仍然认为它比Jon更简单: - )