In this article, we will take a closer look at the techniques we can use to count the number of vowels in a string in C#. We will go through the code and learn more about each approach.
Let’s get started.
Prepare Our Counters Application
First, let’s create a VowelCounters
console application and update the Program.cs
file:
const string vowels = "AEIOUaeiou"; var vowelsAsSpan = vowels.AsSpan(); var vowelsHash = new HashSet<char>(vowels); SearchValues<char> vowelsSearchValues = SearchValues.Create(vowelsAsSpan); const string sentence = "In the vast expanse of the universe, countless galaxies swirl in a cosmic dance," + " each telling a unique story of creation and destruction."; var sentenceAsSpan = sentence.AsSpan();
Inside the Program.cs
file, we store our unique collection of case-insensitive vowels in two variables: vowelsAsSpan
and vowelsHash
. Next, we define our example sentence in two variables: sentence
and sentenceAsSpan
. Lastly, we declare a vowelsSearchValues
variable with the type of SearchValues<char>
.
Now, with our setup done, let’s examine different ways to count vowels within a string.
Count the Number of Vowels Using a Loop
When we want to count something in programming, the first thing that often comes to mind is looping or iteration. Let’s see how to accomplish our goal using both for
and foreach
loops:
public static int CountVowelsUsingForLoop(ReadOnlySpan<char> sentence, ReadOnlySpan<char> vowels) { var total = 0; for (var i = 0; i < sentence.Length; i++) { if (vowels.Contains(sentence[i])) { total++; } } return total; }
Here we pass in the string
to search as a ReadOnlySpan<char>
and a ReadOnlySpan<char>
containing our vowels. We then iterate each character of our sentence
, checking to see if it is contained within our vowels
collection. If it is contained, we increment the total
. Finally, we return the total
.
We can take the same approach using a foreach
loop:
public static int CountVowelsUsingForEachLoop(ReadOnlySpan<char> sentence, ReadOnlySpan<char> vowels) { var total = 0; foreach (var c in sentence) { if (vowels.Contains(c)) { total++; } } return total; }
In essence, our foreach
loop method is identical to the for
loop we previously wrote. We loop through each character and increment the total
when the character is found within the vowels
collection.
Let’s see them in action:
Console.WriteLine("The number of vowels counted using For loop is: " + $"{VowelCounters.CountVowelsUsingForLoop(sentenceAsSpan, vowelsAsSpan)}"); Console.WriteLine("The number of vowels counted using ForEach loop is: " + $"{VowelCounters.CountVowelsUsingForEachLoop(sentenceAsSpan, vowelsAsSpan)}");
Here we see the results from the two methods:
The number of vowels counted using For loop is: 46 The number of vowels counted using ForEach loop is: 46
Count the Number of Vowels Using SearchValues
In this example, we will use a new type SearchValues
that was added in .NET 8 to optimize the performance when searching for values. SearchValues<T> instances are optimized for situations where the same set of values is frequently used for searching:
public static int CountVowelsUsingSearchValues(ReadOnlySpan<char> sentence, SearchValues<char> vowelsSearchValues) { var total = 0; for (var i = 0; i < sentence.Length; ++i) { if (vowelsSearchValues.Contains(sentence[i])) ++total; } return total; }
We are again using for
loop with an if condition that adds to the total vowel count when the character from the sentence is contained in the vowelsSearchValues
.
In our Program.cs
file we call our function within Console.WriteLine
:
Console.WriteLine($"The number of vowels counted using SearchValues is: {VowelCounters.CountVowelsUsingSearchValues(sentenceAsSpan, vowelsSearchValues)}");
When we run the application we see the console output:
The number of vowels counted using SearchValues is: 46
Count the Number of Vowels With a Switch Statement
In our next approach, we will use a switch
statement to count vowels in the supplied string:
public static int CountVowelsUsingSwitchStatement(ReadOnlySpan<char> sentence) { var total = 0; foreach (var c in sentence) { switch (c) { case 'A': case 'a': case 'E': case 'e': case 'I': case 'i': case 'O': case 'o': case 'U': case 'u': total++; break; default: break; } } return total; }
Since the switch
statement is a conditional statement that resembles if-else-if conditions, we first need to supply it with the character for evaluation. So once again we use a foreach
loop to iterate over each character of our string. Next, we use the switch statement to check whether the character is a vowel or not. In case of a match, we increment the total
. Lastly, we return the count.
So, let’s test it out:
Console.WriteLine($"The number of vowels counted using Switch statement is: {VowelCounters.CountVowelsUsingSwitchStatement(sentence)}");
We get the console output:
The number of vowels counted using Switch statement is: 46
Count the Number of Vowels Using Regex.Count()
Regular expressions are a powerful tool when it comes to matching characters in a string. We can use System.Text.RegularExpressions
namespace to enable regular expressions pattern matching and counting the vowels with a one-liner:
[GeneratedRegex(@"[AEIOUaeiou]")] private static partial Regex _regexVowels(); public static int CountVowelsUsingRegexCount(string sentence) { return _regexVowels().Count(sentence); }
Instead of passing the collection of vowels as a parameter, we use a Source Generated Regex to match characters from the supplied string. We call Regex.Count()
to count the number of matches within the string.
Let’s see it in action:
Console.WriteLine($"The number of vowels counted using RegexCount is: {VowelCounters.CountVowelsUsingRegexCount(sentence)}");
And here we see the expected result:
The number of vowels counted using RegexCount is: 46
Count the Number of Vowels Using Regex.Replace()
Our next approach uses Regex.Replace()
and the string.Length
property to count vowels:
[GeneratedRegex(@"[^AEIOUaeiou]+")] private static partial Regex _regexNotVowels(); public static int CountVowelsUsingRegexReplaceAndLength(string sentence) { return _regexNotVowels().Replace(sentence, "").Length; }
We will again use a Source Generated Regex, but this time we define an expression that contains only non-vowels. We then use Replace()
to replace all matching sequences with an empty string. The resulting string will contain only vowels, so we simply return its Length
to get our vowel count:
Console.WriteLine($"The number of vowels counted using Regex Replace and Length is:{VowelCounters.CountVowelsUsingRegexReplaceAndLength(sentence)}");
We then use the Console.WriteLine
to get the console output:
The number of vowels counted using Regex Replace and Length: 46
Count the Number of Vowels Using LINQ
The last approach we will showcase in this article is using LINQ or Language Integrated Query with a lambda function:
public static int CountVowelsUsingLinq(string sentence, HashSet<char> vowels) { return sentence.Count(x => vowels.Contains(x)); }
In this method, we use the Count()
method to count the number of elements that satisfy our condition. The condition is a lambda function that checks if our vowel collection contains the character from the supplied string:
Console.WriteLine($"The number of vowels counted using LINQ is: {VowelCounters.CountVowelsUsingLinq(sentence, vowelsHash)}");
Once again we see the expected result:
The number of vowels counted using LINQ is: 46
Comparing the Performance
Different approaches so far differ only in syntax. So to make any worthy conclusion, we need to measure the execution times to get the whole picture. We use the BenchmarkDotNet package to track the performance of our methods:
| Method | Mean | Error | StdDev | Ratio | RatioSD | Rank | |-------------------------------------- |------------:|---------:|---------:|------:|--------:|-----:| | CountVowelsUsingSearchValues | 98.34 ns | 0.307 ns | 0.272 ns | 1.00 | 0.00 | 1 | | CountVowelsUsingSwitchStatement | 257.18 ns | 4.741 ns | 5.269 ns | 2.61 | 0.06 | 2 | | CountVowelsUsingForLoop | 374.66 ns | 5.044 ns | 4.718 ns | 3.81 | 0.05 | 3 | | CountVowelsUsingForEachLoop | 391.99 ns | 3.519 ns | 3.119 ns | 3.99 | 0.04 | 4 | | CountVowelsUsingLinq | 760.63 ns | 7.728 ns | 6.454 ns | 7.74 | 0.07 | 5 | | CountVowelsUsingRegexCount | 1,099.87 ns | 5.914 ns | 5.243 ns | 11.18 | 0.07 | 6 | | CountVowelsUsingRegexReplaceAndLength | 1,493.22 ns | 7.293 ns | 6.465 ns | 15.19 | 0.08 | 7 |
The results show us that the SearchValues
approach is the fastest, followed by the switch
statement. As we would expect, we see that the for
and foreach
loop approaches have similar execution times, but unfortunately, they are nearly four times as slow as the SearchValues
method. After this, we have our LINQ approach at approximately 6.5x slower. Then rounding out the final positions, we see our approaches using Regular Expressions.
Conclusion
In this article, we discussed several approaches for counting the number of vowels in the string. While there are also notable differences in performance between the methods, we should always consider our use case and code maintainability when choosing a method.