Find the vowels in a string with C# .NET

Here comes a classic developer interview question: write a function that accepts a string and returns the number of vowels that it contains. The vowels in the English language are the following: a, e, i, o u. We want the vowel search to be case-insensitive, i.e. AEIOU must also be counted. This should be a fairly simple problem with multiple solutions. We’ll look at three of them in this post but there are certainly more:

  • using a counter
  • using a regex
  • LINQ

Let’s start with the class skeleton:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Text.RegularExpressions;

namespace Algorithms
{
    public class CountVowels
    {
        public int FindVowels(string input)
        {
            return 0;
        }        
    }
}

Here comes a short set of unit tests:

using Microsoft.VisualStudio.TestTools.UnitTesting;
using Algorithms;

namespace Algorithms.Tests
{
    [TestClass]
    public class CountVowelsTests
    {
        [TestMethod]
        public void CountVowelsTest()
        {
            var findVowels = new CountVowels();
            Assert.AreEqual(0, findVowels.FindVowels(null));
            Assert.AreEqual(3, findVowels.FindVowels("Hello World!!!"));
            Assert.AreEqual(5, findVowels.FindVowels("afdfgehhiddfgOdfgdU"));
            Assert.AreEqual(3, findVowels.FindVowels("UOE"));
            Assert.AreEqual(0, findVowels.FindVowels("rtyplkjhgfdszxcvb"));
        }
    }
}

Let’s start with the most basic counter-based solution that most developers came across in an intro course:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Text.RegularExpressions;

namespace Algorithms
{
    public class CountVowels
    {
        public int FindVowels(string input)
        {
            return FindVowelsWithHelpArray(input);            
        }

        private int FindVowelsWithHelpArray(String inputString)
        {
            int vowelCounter = 0;

            if (inputString != null)
            {
                var vowels = new List<char>() { 'a', 'e', 'i', 'o', 'u' };
                foreach (char c in inputString.ToLower())
                {
                    if (vowels.Contains(c))
                    {
                        vowelCounter++;
                    }
                }
            }            

            return vowelCounter;
        }        
    }
}

We initialise the counter variable vowelCounter to 0. Then if the input string is not null then we create a list of chars to contain all the vowels we want to count. Then we iterate through the lower-cased string char by char and if the current char is part of the vowel list then we increase the counter. Finally we return the counter from the function.

Run the unit tests, they all pass.

Next, let’s take a look at the Regex solution:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Text.RegularExpressions;

namespace Algorithms
{
    public class CountVowels
    {
        public int FindVowels(string input)
        {
           //return FindVowelsWithHelpArray(input);
           return FindVowelsWithRegex(input);
        }

        private int FindVowelsWithHelpArray(String inputString)
        {
            abridged...
        }

        private int FindVowelsWithRegex(String inputString)
        {
            if (inputString == null) return 0;
            return Regex.Matches(inputString, "[aeiou]", RegexOptions.IgnoreCase).Count;
        }        
    }
}

We use the Regex.Matches (https://docs.microsoft.com/en-us/dotnet/api/system.text.regularexpressions.regex.matches?view=netcore-3.1) function to count all the matches in the incoming string. The match is based on a simple array that reflects the vowels that we want to find. I took a shortcut and added the built-in RegexOptions enumeration to make sure that the search is case-insensitive instead of expressing it directly in the Regex string.

Run the unit tests, they will all still pass.

Finally let’s see a LINQ-based solution. LINQ is very versatile and there are certainly multiple ways to solve this problem using LINQ expressions. We’ll go for the most straightforward one using the Count() function:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Text.RegularExpressions;

namespace Algorithms
{
    public class CountVowels
    {
        public int FindVowels(string input)
        {
           //return FindVowelsWithHelpArray(input);
           //return FindVowelsWithRegex(input);
           return FindVowelsWithLinq(input);
        }

        private int FindVowelsWithHelpArray(String inputString)
        {
            abridged
        }

        private int FindVowelsWithRegex(String inputString)
        {
            abridged
        }

        private int FindVowelsWithLinq(String inputString)
        {
            if (inputString == null) return 0;
            var vowels = new List<char>() { 'a', 'e', 'i', 'o', 'u' };
            return inputString.ToLower().Count(c => vowels.Contains(c));
        }
    }
}

We again use the list of vowels just like in the first solution. Then we use the Count() LINQ function to count the vowels. We do that by providing a lambda function to go through each character in the string and see if the vowel list contains the given character.

The unit tests will pass just like before.

Advertisement

Capitalise each word in a sentence using C# .NET

Here comes a classic developer interview problem: write a function that takes a string as a parameter and returns the same string with each word in that string capitalised. Examples:

InputOutput
hello worldHello World
a beautiful morningA Beautiful Morning
the show must go onThe Show Must Go On
i can’t help falling in love with youI Can’t Help Falling In Love With You

There are of course multiple solutions to this problem. In this post we’ll go through two of them. Since this blog is mostly dedicated to C# and .NET we’ll use C# but the solutions can easily be translated into other OOP languages.

Note that a third solution based on regex was posted by http://gravatar.com/wkinkeldei in the comment section below.

We can start with a skeleton class and a small set of unit tests.

Capitalise.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Algorithms
{
    public class Capitalise
    {
        public string CapitaliseWordsInSentence(string input)
        {
            return null;
        }        
    }
}

…and a corresponding set of unit tests:

using Algorithms;
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Algorithms.Tests
{
    [TestClass]
    public class CapitaliseTests
    {
        [TestMethod]
        public void CapitaliseWordsInSentenceTests()
        {
            Capitalise c = new Capitalise();
            Assert.AreEqual("Hello World", c.CapitaliseWordsInSentence("hello world"));
            Assert.AreEqual("A Beautiful Morning", c.CapitaliseWordsInSentence("a beautiful morning"));
            Assert.AreEqual("The Show Must Go On", c.CapitaliseWordsInSentence("the show must go on"));
            Assert.AreEqual("I Can't Help Falling In Love With You", c.CapitaliseWordsInSentence("i can't help falling in love with you"));
        }        
    }
}

If you run the unit tests then they will of course fail:

Assert.AreEqual failed. Expected:<Hello World>. Actual:<(null)>.

The first possible solution we’ll be looking at is based on a simple iteration. We iterate through each character in the input string. As soon as the character before the current one is equal to an empty space we capitalise the current char. Add the following function to the Capitalise class:

namespace Algorithms
{
    public class Capitalise
    {
        public string CapitaliseWordsInSentence(string input)
        {
            return CapitaliseWordsWithIteration(input);
        }

        private string CapitaliseWordsWithIteration(string input)
        {
            StringBuilder stringBuilder = new StringBuilder(input[0].ToString().ToUpper());
            for (int i = 1; i < input.Length; i++)
            {
                if (input[i - 1] == ' ')
                {
                    stringBuilder.Append(input[i].ToString().ToUpper());
                } else
                {
                    stringBuilder.Append(input[i]);
                }
            }
            return stringBuilder.ToString();
        }     
    }
}

We use a StringBuilder to build the return string character by character. By default we capitalise the first character to begin with. Then we start the iteration from the second character of the input string until its end. If the character before the current one, i.e. at position i - 1 is a whitespace then we capitalise the current char. Otherwise we just add the char to the StringBuilder as-is. Finally we return the final string.

Let’s re-run the unit tests:

OK, good.

Let’s try another approach based on substrings. We split the input string using a whitespace delimiter. Then we iterate through each element in the delimited array. We take the first character of each word, capitalise it, and push the rest of the word to it. We collect the capitalised words in a list which we then use to rebuild the sentence using string.Join. Here’s the function:

private string CapitaliseWordsWithSubstrings(string input)
        {
            List<string> elements = new List<string>();
            foreach(string s in input.Split(' '))
            {               
                elements.Add(s[0].ToString().ToUpper() + s.Substring(1));   
            }

            return string.Join(" ", elements);
        }

If you call this function from CapitaliseWordsInSentence then the tests should pass just like before:

public string CapitaliseWordsInSentence(string input)
{
      return CapitaliseWordsWithSubstrings(input);
     //return CapitaliseWordsWithIteration(input);
}

We can improve the CapitaliseWordsInSentence by adding guards against nulls and empty strings:

public string CapitaliseWordsInSentence(string input)
{
    if (string.IsNullOrEmpty(input))
    {
        return "";
    }
    return CapitaliseWordsWithSubstrings(input);
    //return CapitaliseWordsWithIteration(input);
}

That’s it really.

Pattern matching in C# 7.0

C# 7.0 introduces a new language feature called pattern matching. If you are familiar with F# then you’ll know what pattern matching is about. Pattern matching is used extensively in F# but has not been available in C# before 7.0. Also, pattern matching is not just a simple feature in F#, but more like a wide range of features. However, C# 7.0 has not yet caught up with all that. C# 7.0 only introduces a small set of features behind pattern matching and can be regarded as syntactic sugar, but it’s a good start. I’d expect that the same F# features will eventually be transferred into C# as well in upcoming updates.

Pattern matching is most often used in “if” or “switch” branches to see if an incoming value matches a certain pattern. If it does then we take that path, otherwise we take another.

Let’s start with a starting point with a base class and two derived classes:

Read more of this post

More on deconstruction in C# 7

We looked at deconstruction using the new ValueTuple type in this post. This short post only shows a couple more bits and pieces related to object deconstruction in C# 7.

Consider the following Rectangle class:

public class Rectangle
{
	public int Width { get; set; }
	public int Height { get; set; }
}

Let’s see if we can collect the Width and Height properties into a tuple:

Read more of this post

New Tuple features and deconstruction with Tuples in C# 7

Tuples have been available in C# for some time now. C# 7 comes with a handful of new things for tuples though.

Let’s see an example for using tuples in the old way. The following function returns a Tuple with two integers: the first is the total days of a duration and the second is the total hours of a duration. Not the most useful function that the world has ever seen, but it’s good for demo purposes:

private Tuple<int, int> AnalyseDatesOld(DateTime first, DateTime second)
{
	TimeSpan timespan = (second - first).Duration();
	return new Tuple<int, int>(Convert.ToInt32(timespan.TotalDays), Convert.ToInt32(timespan.TotalHours));
}

Read more of this post

Expression bodied members in constructors and get-set properties in C# 7.0

Expression bodied members were introduced in C# 6 for methods and properties. This feature has been extended to constructors and getters/setters in C# 7.0.

Here’s an example:

public class Dog
{
	private string name;

	public Dog(String name) => this.name = name;	

	public string NameFormatted
	{
		get => name.ToUpper();
		set => name = value.ToUpper();
	}

	public string Name
	{
		get => name;
	}
}

View all various C# language feature related posts here.

Handling out variables in C# 7.0

Most of you will be familiar with “out” variables in C#. They are often misused to return multiple values from a function and can indicate a code smell.

Nevertheless they exist and 7.0 provides some new syntax in this area.

Here’s a typical example from the well-known built-in TryParse function from before C# 7.0:

public void OldWay()
{
	int number;
	if (int.TryParse("1234", out number))
	{
		Console.WriteLine(number);
	}
}

We instantiate “number”, pass it to TryParse and it will be populated with 1234 if the parse succeeds.

C# 7 saves us from the necessity of declaring a variable up front in the following way:

public void NewWay()
{
	//include out param in expression
	if (int.TryParse("1234", out int number)) //works with "var" as well as with the concrete object type
	{
		Console.WriteLine(number);
	}
	Console.WriteLine(number);
}

So we declare “number” in an expression and it will be available outside the “if” block as well.

If parsing fails then the declared variable will get a default value. Numbers get 0, reference types “null” etc.

View all various C# language feature related posts here.

Using delimiters for numeric literals in C# 7.0

Say you need to work with relatively large numbers in your C# code:

int number = 3452123;

Wouldn’t it be nice if you could use some kind of delimiter to indicate the millions, thousands etc? Like in 3,452,123? In C# 7.0 the underscore provides a solution for this:

int number = 3_452_123;

The underscores will be ignored by the compiler.

View all various C# language feature related posts here.

Embedded local functions in C# 7.0

Occasionally it happens that we want to group related code within a function without creating another private function for that. C# 7.0 provides a neat way for that in the form of local functions which are functions within functions.

Consider the following example:

public class LocalFunctionsDemo
{
	public void RunDemo()
	{
		int GetDiff(DateTime first, DateTime second)
		{
			TimeSpan ts = (second - first).Duration();
			return Convert.ToInt32(ts.TotalDays);
		}

                int dayDiff = GetDiff(DateTime.UtcNow, DateTime.UtcNow.AddHours(10));
			
		Console.WriteLine(dayDiff);
	}
}

Read more of this post

Access modifiers in C# 7.2

C# 7.2 comes with a new access modifier “private protected”. It sounds like a weird combination so let’s see how the various access modifiers in the current release of C# work.

Let’s start with an abstract class called Animal:

public abstract class Animal
{
	private String privateName = "Private name";
	protected String protectedName = "Protected name";
	protected internal String protectedInternalName = "Protected internal name"; 
	private protected String privateProtectedName = "Private protected name";
	public String publicName = "Public name";
}

Read more of this post

Elliot Balynn's Blog

A directory of wonderful thoughts

Software Engineering

Web development

Disparate Opinions

Various tidbits

chsakell's Blog

WEB APPLICATION DEVELOPMENT TUTORIALS WITH OPEN-SOURCE PROJECTS

Once Upon a Camayoc

Bite-size insight on Cyber Security for the not too technical.

%d bloggers like this: