Ops Scripting w. Ruby: Frequency 2

Tracking Frequency in Ruby: Part II

This is continuation of series on building Ruby frequency hash to solve common problems in operations scripting. Previously, I described the problem as well as gave a sample data file.

Now, we’ll start to discuss solutions and discussions about Ruby language features.

The Solutions

These solutions we’ll use a conditional while loop in conjunction with the gets method. The gets will return false once it gets an EOL (end-of-line) marker, otherwise, it returns true.

Solution 1: Conditionally Initialize new Hash Value

We open the file and iterate line by line in this example:

We only care about the shell in each line, so we can do a few operations to extract the line. First, chomp off the newline, then split the line into a list, where we slice off the 7th item (indexed by 6):

line = line.chomp            # strip newline
line_items = line.split(/:/) # split up line by ':' divider
shell = line_items[6] # slice off 7th item

This can all be done in a single line.

shell = (line.chomp.split(/:/))[6]

Now that we extracted the shell, we need to check if we actually got a shell.

if shell != nil
# do stuff with that shell as a key
end

Each item in the counts hash will have a key that represents the shell, and a value that represents frequency of shell used in our data file passwd.

We simply need to increment the value. As Ruby does not initialize values to a default value when first used, we have to do this process manually.

if counts.has_key? shell
counts[shell] += 1 # increment existing value
else
counts[shell] = 1 # initialize value first time
end

Alternatively, we could write the same branch logic using ternary operator ?::

counts[shell] = counts.has_key?(shell) ? counts[shell] += 1 : 1

Solution 2: Auto Assign a Default Value

Instead of conditionally setting the frequency count with branch logic, we can use ruby’s ||= operator, which sets a default value for a key that does not yet exist..

In our solution, we set the default of a key that does not yet exist to 0, and then increment that value by one.

counts[shell] = (counts[shell] ||= 0) + 1

We also only do this whole operation if we have a valid shell.

counts[shell] = (counts[shell] ||= 0) + 1 if shell != nil

Solution 3: Hash with Default Value

Another method is to just to set all keys that do not yet exist to have a default. We can do this with the Hash.new() method instead of {} to create an empty hash.

After this, we no longer need logic to handle conditionally initializing a new hash item.

This allows ruby to behave like other languages that have defaults, but gives us greater control to set the default to one of our choosing.

Conclusion

From these solutions, the you should have picked up the following takeaways for Ruby:

  • Iterating through a file with while and gets
  • Splitting a String
  • List Slicing (or indexing in this case)
  • Testing variable is initialized
  • 3 ways to initialize default value in hash class

In the next article, I will show how to use functional programming style to this problem with map() and select().