Embracing efficiency in refactoring your Ruby code

Photo by John Barkiple on Unsplash

One of my goals as a new programmer is to write code that is both functional and clear. Clearly written code has the double benefit of being easier for other developers to follow and saving me from typing more than I need to! In Ruby, thanks to the language’s flexibility, there are several ways to meet that goal. In this blog post, I will discuss three: using the “yield” keyword, using curly brackets instead of “do…end”, and using variables judiciously. Each of these aspects of Ruby cut down on fluff and allow programmers to write less verbose, more readable code.

At a high level, the yield keyword take the place of a block of code in Ruby. It literally tells the code to “yield” for the block that is being passed in. So far, I have mostly used the yield keyword to pass a block of code into an enumerable method called on an array or hash object. Consider the following code, which does not use yield:

numbers_array = [1, 2, 3, 4]def add_one(array)
array.map do |item|
item + 1
end
end
add_one(numbers_array)# =>[2, 3, 4, 5]def subtract_one(array)
array.map do |item|
item — 1
end
end
subtract_one(numbers_array)# =>[0, 1, 2, 3]def double_nums(array)
array.map do |item|
item * 2
end
end
double_nums(numbers_array)# => [2, 4, 6, 8]

This code works, but it’s repetitive. Three separate methods are defined, yet they only differ by the operation being passed into the .map method. We can do better. Let’s refactor the code to make use of the yield keyword:

numbers_array = [1, 2, 3, 4]def reduce(array)
array.map do |item|
yield(item)
end
end
reduce(numbers_array) do |item|
item + 1
end
# => [2, 3, 4, 5]reduce(numbers_array) do |item|
item — 1
end
# => [0, 1, 2, 3]reduce(numbers_array) do |item|
item * 2
end
# => [2, 4, 6, 8]

Better. As you can see, using yield means we only have to define one method (reduce) and we can perform all the previous operations simply by passing in a block to the method call. When we call the reduce method, it yields to the passed in block as it maps over each element in the numbers_array — also passed to the method.

In the above code, I used the Ruby keyword “do” to tell Ruby that my code block was starting and the keyword “end” to tell Ruby to terminate it. The same thing can be accomplished by using curly brackets:

numbers_array = [1, 2, 3, 4]def reduce(array)
array.map {|item| yield(item)}
end
reduce(numbers_array){|item| item + 1}# => [2, 3, 4, 5]reduce(numbers_array){|item| item — 1}# => [0, 1, 2, 3]reduce(numbers_array){|item| item * 2}# => [2, 4, 6, 8]

Compared to do…end, there’s no difference in functionality, but using this syntax cuts down on the (already short) do…end syntax. This can make a world of difference visually when you are, for example, using several enumerables nested within conditionals within a method in Ruby.

The above code looks good to me and I’m pretty happy with it, but I want to make one last tweak. Here, I’ve somewhat arbitrarily used “item” to refer to each item in the numbers_array. I’m going to shorten that further to “n”, which is not only more concise but also more descriptive of the numbers that I’m passing into the reduce method and yield blocks:

numbers_array = [1, 2, 3, 4]def reduce(array)
array.map {|n| yield(n)}
end
reduce(numbers_array){|n| n + 1}# => [2, 3, 4, 5]reduce(numbers_array){|n| n — 1}# => [0, 1, 2, 3]reduce(numbers_array){|n| n * 2}# => [2, 4, 6, 8]

Alright! Our code still works and it’s less wordy, more efficient, and more descriptive than what we started with. Hopefully, you’ve found this post helpful in thinking about ways to refactor code!

Web developer and lawyer

Web developer and lawyer