繁体   English   中英

字符串的集合到字典

[英]Collection of strings to dictionary

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

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

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

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

最好一次通过琴弦收集......

var dico = strings.GroupBy(x => x).ToDictionary(x => x.Key, x => x.Count());

Timwi /达林的建议将在单传过来的原始集合执行此,但它将会为集团多个缓冲区。 LINQ并不是非常擅长这种计数,这样的问题是我编写Push LINQ的最初动机。 您可能希望阅读我的博客文章 ,了解有关为什么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),除了在写入值时涉及字典查找。 另一种实现方式是使用foreach和一个从null开始的current值...但最终在其他几种方式上变得非常狡猾。 (我已经尝试过了:)当我需要第一个值的特殊情况处理时,我通常会使用上面的模式。

实际上你可以使用Aggregate使用LINQ来做到这一点,但它会非常讨厌。

标准LINQ方式是这样的:

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

如果这是实际的生产代码,我会选择Timwi的回复

如果这确实是功课,并且你应该编写自己的实现,那就不应该太难了。 这里只是一些提示,指出你正确的方向:

  1. Dictionary<TKey, TValue>有一个ContainsKey方法。
  2. IDictionary<TKey, TValue>接口的this[TKey]属性是可设置的; 即,你可以做dictionary[key] = 1 (这意味着你也可以做dictionary[key] += 1 )。

从这些线索中我认为你应该能够“手工”地弄清楚如何做到这一点。

如果您正在寻找一种特别有效 (快速)的解决方案,那么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;

这是一个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更简单:-)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM