This becomes easier if you break it into pieces. What you need is a function that takes a string and tells you if the characters in the string are in alphabetical order.
For example:
public static class CharacterSequence // I didn't think hard about the name
{
public static bool CharactersAreInAlphabeticalOrder(string input)
{
return input.SequenceEqual(input.OrderBy(c => c));
}
}
Having done that, the next part is just checking a collection of strings and returning only the ones where the characters are in order. If
A string
is a collection of characters (char
). This method takes the sequence of characters and sorts it. Then it compares the original to the sorted. If they are the same, then the original sequence was in order.
var wordsWithCharactersInOrder =
words.Where(CharacterSequence.CharactersAreInAlphabeticalOrder);
One reason why it's helpful to break it up like this is that it's easier to understand. It's very easy to read the above code and tell what it does. Also, if you realize that there's something you want to change about the way you check for characters in order, you can change that in the smaller function.
For example, you might realize that the original function is case-sensitive. C comes before d, but D comes before c. In this example it's less noticeable because the function is small, but as logic becomes more complex it's easier to read and think about when we break things into smaller functions. The case-insensitive version would be
public static bool CharactersAreInAlphabeticalOrder(string input)
{
var lowerCase = input.ToLower();
return lowerCase.SequenceEqual(lowerCase.OrderBy(c => c));
}
If you want to go a step further then you can compare the characters one at a time instead of sorting the entire string.
public static bool CharactersAreInAlphabeticalOrder(string input)
{
if (input.Length < 2) return true;
var lowerCase = input.ToLower();
var characterIndexes = Enumerable.Range(0, input.Length - 1);
return characterIndexes.All(characterIndex =>
lowerCase[characterIndex] <= lowerCase[characterIndex + 1]);
}
You can also write unit tests for it. If you know that the smaller function always returns the expected results, then the larger one that checks a collection of strings will return the correct results.
Here's an example of a unit test. It's much easier to test lots of conditions this way and have confidence that the function works than to edit the code and run it over and over. If you realize that there's another case you have to account for, you can just add it.
[DataTestMethod]
[DataRow("A", true)]
[DataRow("AB", true)]
[DataRow("abc", true)]
[DataRow("aBc", true)]
[DataRow("ba", false)]
public void CharactersAreInAlphabeticalOrder_returns_expected_result(string input, bool expected)
{
var result = CharacterSequence.CharactersAreInAlphabeticalOrder(input);
Assert.AreEqual(expected, result);
}
There was a small error in my original code. It didn't work if a word had only two letters. Without the test that error could have gone into the application without being noticed until later when it would take longer to find and fix. It's much easier with a test.