字符串的集合到字典

时间:2010-10-24 18:30:07

标签: c# linq c#-4.0

给定一个有序的字符串集合:

var strings = new string[] { "abc", "def", "def", "ghi", "ghi", "ghi", "klm" };

使用LINQ创建字符串字典到集合中该字符串的出现次数:

IDictionary<string,int> stringToNumOccurrences = ...;

最好在琴弦集合中一次性完成此操作......

6 个答案:

答案 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答案:)

编辑:我没有注意到你的琴弦是订购的。这意味着你可以更多更有效率,因为你知道一旦你看到字符串x然后字符串y,如果x和y不同,你将永远不会再看到x。 LINQ中没有任何东西可以让这个变得特别容易,但是你可以很容易地自己做到这一点:

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 字典查找。另一种实现方式是使用foreachcurrent值从null开始...但最终在其他几种方式上变得非常狡猾。 (我已经尝试过了:)当我需要第一个值的特殊情况处理时,我通常会使用上面的模式。

实际上你可以使用Aggregate使用LINQ执行此操作,但这会非常讨厌。

答案 2 :(得分:3)

标准的LINQ方式是:

stringToNumOccurrences = strings.GroupBy(s => s)
                                .ToDictionary(g => g.Key, g => g.Count());

答案 3 :(得分:0)

如果这是实际的生产代码,我会选择Timwi's response

如果这确实是家庭作业,并且你应该编写自己的实现,那就不应该太难了。以下是一些指示您指向正确方向的提示:

  1. Dictionary<TKey, TValue>ContainsKey方法。
  2. IDictionary<TKey, TValue>接口的this[TKey]属性是可设置的;即,你可以dictionary[key] = 1(这意味着你也可以dictionary[key] += 1)。
  3. 从这些线索中我认为你应该能够“手工”弄清楚如何做到这一点。

答案 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更简单: - )