Подсчет количества символов
Я написал код, который использует SortedDictionary
, чтобы подсчитать количество вхождений символа в строке.
Как я могу улучшить этот код? Что я должен делать по-другому? Любые советы вообще были бы полезны.
static void Main(string[] args)
{
string longText = @"The quick brown fox jumps over the lazy dog";
var count = CharacterCount.Count(longText);
foreach (var character in count)
{
Console.WriteLine("{0} - {1}", character.Key, character.Value);
}
}
Класс CharacterCount:
class CharacterCount
{
public static SortedDictionary<char, ulong> Count(string stringToCount)
{
SortedDictionary<char, ulong> characterCount = new SortedDictionary<char, ulong>();
foreach (var character in stringToCount)
{
if (!characterCount.ContainsKey(character))
{
characterCount.Add(character, 1);
}
else
{
characterCount[character]++;
}
}
return characterCount;
}
Вот результат:
5 ответов
Вы можете сделать то же самое с LINQ :
var counts = longText.GroupBy(c => c) // put each character into a "bucket"
// order the buckets alphabetically
.OrderBy(c => c.Key);
// then convert to dictionary where key = character, value = count
.ToDictionary(grp => grp.Key, grp => grp.Count())
Для удобства /удобочитаемости вы можете превратить это в метод расширения в string
:
static class Extensions
{
public static Dictionary<char, int> CharacterCount(this string text)
{
return text.GroupBy(c => c)
.OrderBy(c => c.Key)
.ToDictionary(grp => grp.Key, grp => grp.Count());
}
}
И вызовите его вот так:
var longText = @"The quick brown fox jumps over the lazy dog";
var counts = longText.CharacterCount();
Однако обратите внимание, что вышеописанный не создает словарь отсортированный в том смысле, что если впоследствии вы добавите ему еще один ключ /значение, заказ больше не будет поддерживаться. Вы можете повторно сортировать в этот момент или использовать этот SortedDictionary
, чтобы создать отсортированный словарь.
Вы можете избавиться от переменной count
, поскольку она здесь не нужна и просто добавляет беспорядок в main
, вы просто вызываете метод .Count
внутри объявление foreach.
, так что:
static void Main(string[] args)
{
string longText = @"The quick brown fox jumps over the lazy dog";
var count = CharacterCount.Count(longText);
foreach (var character in count)
{
Console.WriteLine("{0} - {1}", character.Key, character.Value);
}
}
становится этим
static void Main(string[] args)
{
string longText = @"The quick brown fox jumps over the lazy dog";
foreach (var character in CharacterCount.Count(longText))
{
Console.WriteLine("{0} - {1}", character.Key, character.Value);
}
}
Вы используете ulong
в вашем SortedDictionary<T,TK>
, это представляет собой максимальное значение 18 446 744 073 709 551 615, что, по-видимому, немного завышено для вашей ситуации. Я уверен, что int
будет достаточно, так как максимальное значение равно 2 147 483 647.
Кроме того, ваш метод с именем Count
, трудно предположить, что он вернет код SortedDictionary<char,ulong>
. Я думаю, что он вернет код int
, так как все методы с именем Count
в .Net Framework. Вы можете назвать его GetNumberOfOccurencePerCharacter
, в противном случае это выглядит как Count
вернет число символов в string
вместо количества раз, когда каждый символ появляется в string
Есть одна фундаментальная вещь, которую этот код не обрабатывает. Хотя вы, возможно, выбрали это ограничение по назначению, многие люди не совсем осознают это, поэтому я подниму его. Существует фундаментальное различие между «символом» и ---- +: = 0 =: + ----. Ваш вопрос и код рассматривают их как одно и то же. Что может быть хорошо для ваших целей, но не будет обрабатывать все возможные символы в Юникоде.
В Unicode то, что люди обычно считают персонажем, называется grapheme. Каждая графема представлена одним или несколькими последовательными значениями char
. Некоторые графемы даже имеют несколько возможных представлений. В .NET на самом деле очень просто выделить строку в своих графемах, используя StringInfo. GetTextElementEnumerator . Но это касается только первого предостережения. Второе предупреждение (несколько представлений) можно обработать с помощью String.Normalize .
Я считаю, что эти вызовы могут быть объединены с подходом LINQ, предложенным Анной Лир, но я еще не подтвердил, собрав его вместе.
(См. также мой соответствующий ответ по аналогичному вопросу.)
Хотя этот вопрос устарел, никто не упомянул, что с помощью ContainsKey()
вместе с получателем свойства Item Dictionary<TKey, TValue>
следует заменить вызовом TryGetValue()
, который быстрее потому что проверка, существует ли ключ, также выполняется с помощью Item getter.
Внутри этих трех методов вызывается FindEntry()
, чтобы проверить, существует ли данный ключ. Поэтому вызов этого метода только один раз через метод TryGetValue()
должен быть таким, как это сделать
public static SortedDictionary<char, int> Count(string stringToCount)
{
SortedDictionary<char, int> characterCount = new SortedDictionary<char, int>();
foreach (var character in stringToCount)
{
int counter = 0
characterCount.TryGetValue(character, out counter);
characterCount[character] = counter +1;
}
return characterCount;
}
Я изменил тип TValue
словаря на int
, потому что этого достаточно.