DEV Community

A.J. Romaniello
A.J. Romaniello

Posted on • Edited on

Regex Words, Vowels, Consonants, and Sentences in Ruby

A common algorithm used in web applications is a word counter. Although with ruby, we can use regex to get a ton of information on our text in very few lines of code. I wanted to make a readable and comprehensive guide on how awesome ruby is at handling operations like this. Feel free to look at the code and test it out on repl.it

Creating a TextAnalyzer Class

The first step is to move all of our methods that deal with regexing the string/text and finding the information on it into its own class. In this instance we are going to call it TextAnalyzer.

Our class might look a little something like this at the beginning, allowing us to initialize with a text attribute and then normalizing this all to uppercase (which will help us down the road, lowercase would also work but looks less appetizing).

class TextAnalyzer attr_reader :text def initialize(text) # upcase for ease of counting @text = text.upcase self end end 
Enter fullscreen mode Exit fullscreen mode

Adding Our Basic Methods

Counting Words

Since we have our text as one lengthy string, we simply need to split this text at every space and then count the size of the array we split it into. Giving us the amount of words in our string, we don't care about punctuation or digits only spaces.

Our method would look something like:

def word_count text.split(' ').size end 
Enter fullscreen mode Exit fullscreen mode

Counting Characters

First we need to think of our pre-requisite:

  • A character is any text within a string (including spaces, punctuation, digits, etc)

Since we don't care about what the actual character is, we can just split all of text after every character. Which would look like:

def chars text.split('') end # and to count would be simply def character_count chars.size end 
Enter fullscreen mode Exit fullscreen mode

Counting Letters

Again lets think of our pre-requisites for a letter:

  • Can't be punctuation
  • Can't be a digit
  • Can't be whitespace

Using a sweet tool called Rubular we can test our regex before implementing it. Using the Regex \W will select any non-word character, using String#gsub we can then replace those non-word characters, with empty strings ('').

All together it would look something like:

 def letters text.gsub(/[\W]/, '') end # a little helper method to make the string into an array def letters_array letters.split('') end # counts the letters in our long string def letter_count # don't forget to split our string into an array first! letters_array.size end 
Enter fullscreen mode Exit fullscreen mode

Counting Consonants and Vowels

Again lets check our pre-requisites for a vowel:

  • Must be A, E, I, O, or U

And for consonants:

  • Must not be a vowel

For both operations we can make use of the String#scan method that ruby provides. And once again utilizing the regex tool to either find all vowels, or all consonants. The regex we would use would be [ ] containing the letters we want to search for.

All together the methods would look something like:

 # counts the vowels from our #letters string def vowel_count letters.scan(/[AEIOU]/).size end # counts the consonants from our #letters string def consonant_count # adding ^ before our characters within the scan # will find anything except the given characters letters.scan(/[^AEIOU]/).size end 
Enter fullscreen mode Exit fullscreen mode

It is important to note that because of our operations in our letters method, we don't need to check for punctuation, digits, or whitespace.

Finding the Most Common Letter(s)

In order to find the most common occurrence of a letter, many people build a large while loop or confusing regexes to find the most common occurrence (example). Ruby is simple, expandable, and flexible and we should code that way.

In the case of a tie we wouldn't want our #most_common_letters method to return just the first or last, we want to return all of our ties and then let someone outside our method decide which item they would like to choose. (Whether its first, last, or somewhere in-between!)

 # finds the most common character from our #letters string def most_common_letters char_hash = {} letters_array.each do |c| char_hash[c] ||= 0 unless char_hash[c] char_hash[c] += 1 end # finds the highest occurrence # could use char_has.max_by {|k,v| v} to get the max and character at the same time # although we would rather return ALL in the case of a tie max = char_hash.values.max # returns all in the case of a tie char_hash.map {|k,v| {k => v} unless v < max }.compact end 
Enter fullscreen mode Exit fullscreen mode

I've found the best way to do this by creating a hash to keep track of unique letters as the key (since we are counting them) and then update their value by 1 every time there is a new occurrence of that letter. This will return the most common letter(s) with their count as the value.

Counting the Most Common Letter

Now that we can find our most common letters with the frequency it is up to us how we want to choose our winner in the case of a tie (remember that we are returning an array of letters).

Let's say we want the first tie, we could call most_common_letters.first. Since this is returning an array of hashes (letters with their frequency) we need to specify the letter and the frequency.

Our code would look something like this:

 # holds the key, value pair for the most common letter def most_common # gets the first match from our array  most_common_letters.first end # gets the letter from the most common hash def most_common_letter most_common.keys.first end # gets the most common letters value def most_common_letter_count most_common.values.first end 
Enter fullscreen mode Exit fullscreen mode

Using Our TextAnalyzer

Now we are all set up and ready to go, displaying the results is as easy as initializing with the string and displaying the associated methods.

text_to_analyze = "Hey! Isn't ruby amazing?!?" text = TextAnalyzer.new(text_to_analyze) display_string = <<-STR  Word Count: #{text.word_count} Sentence Count: #{text.sentence_count} Character Count: #{text.character_count} Letter Count: #{text.letter_count} Vowel Count: #{text.vowel_count} Consonant Count: #{text.consonant_count} Most Common Letter: #{text.most_common_letter} used #{text.most_common_letter_count} times. STR puts display_string 
Enter fullscreen mode Exit fullscreen mode

Conclusion

Ruby is awesome! It let's use use regex and hashes to operate on strings and find occurrences under many different variables. If you find yourself looping over a string to find certain occurrences of characters you might want to use regex!

Combining regex with other ruby functionalities such as hashes and loops can make this extremely powerful for keeping track of occurrences of variables or sorting by any set of pre-requisites.

  • View the completed project on repl.it

A Reminder on how to Regex

One way I love to find my regex for the string I am working with is ask myself:

What do I want after the regex? What is going into the regex? Should I look for included characters or excluded characters?

It is very helpful to list out your requirements for the string you want after the regex.

Top comments (0)