Ruby has a lot of built-in methods. Using the right method helps to save effort and improve productivity and quality of work. Below is a list of some interesting but lesser known methods.
1. Integer#digits
This method, introduced in Ruby 2.4, is very useful when working with the digits of an integer, it merges the digits of an integer into an array, and sorts it backwards.
Before digits
, to get the digits of an integer, the typical solution is to convert the number to a string, then cut the string into an array of characters using the chars
method, finally converting the strings back. to an integer:
1 2 3 | 123.to_s.chars.map(&:to_i).reverse => [3, 2, 1] |
With digits, things are much simpler:
1 2 3 | 123.digits => [3, 2, 1] |
2. Object#tap
When you want to create an object, execute some method on the object, then return that object. You can do as follows:
1 2 3 4 | user = User.new user.name = "Test" user |
The above method needs to specify the return object, using a temporary variable.
With tap
, the above example becomes:
1 2 | User.new.tap { |user| user.name = "Test" } |
Here is a useful Ruby method.
3. Array#values_at
Returns an array containing the elements of the original array corresponding to the positions passed. Often used for the purpose of getting multiple unsorted values from an array or a hash.
1 2 3 4 | arr = [1,2,3,4,5] a, b, c = arr.values_at(0, 1, 4) => [1, 2, 5] |
Example on generals with:
1 2 3 4 | arr = [1,2,3,4,5] a, b, c = arr[0], arr[1], arr[4] => [1, 2, 5] |
values_at
also used with Hash:
1 2 3 4 | hash = {a: 10, b: 20} hash.values_at(:a, :b) => [10, 20] |
4. Hash#transform_values
Returns a new Hash with executable block values, unchanged keys. Often used when you want to change all values of Hash.
The solution could be:
1 2 3 4 | hash = {a: 10, b: 20} hash.each { |k, v| hash[k] = v * 2 } => {:a=>20, :b=>40} |
With Ruby version> = 2.4, there is a better way to do this:
1 2 3 | hash.transform_values { |v| v * 2 } => {:a=>40, :b=>80} |
This is a method coming from Rails, but is now available outside the framework.
5. Kernel#itself
Returns the object itself. Useful for chaining methods, such as scope
.
Suppose we have an array with repeating words and want to count the number of repeats. There are many ways to do this, of which the Kernel#itself
can be used.
1 2 3 4 | words = %w(cat cat tiger dog cat) words.group_by(&:itself).each_with_object({}) { |(k,v), hash| hash[k] = v.size } => {"cat"=>3, "tiger"=>1, "dog"=>1} |
In documentation itself
appears under the class Object
, but it’s defined on the module Kernel
.
6. Enumerable#cycle
Block calls for each element of the enum repeatedly n times or forever if none or nil is not given. Returns nil if the loop ends without interruption. If there is no block, an enumerator is returned.
You want to repeat a pattern or convert between two values, Enumerable#cycle
could be the method you are looking for.
1 2 3 4 | array = %w(a b c) array.cycle(3).to_a # same as array * 3 => ["a", "b", "c", "a", "b", "c", "a", "b", "c"] |
1 2 3 | switch = %w(on off).cycle switch.next |
Very useful because there is no need to check the current value to convert to the remaining value. Just call the remaining next
for Ruby to do.
7. String#squeeze
This is a simple method that works on strings, the task is to remove duplicate characters. Calling just squeeze
without passing parameters will get a new string replacing all the same strings with a single character.
1 2 3 | "aabbccdda".squeeze => "abcda" |
It is also possible to pass a range of characters, useful if you only want to delete duplicates of certain characters.
1 2 3 | "aabbbccddeefffggg".squeeze("a-f") => "abcdefggg" |
8. Array#bsearch
When searching through arrays in Ruby, the first method that comes up to be positioned is Enumerable#find
. This method will search the entire array until it finds a match. Ideally, the element to be found is in the first position, requiring O (n) complexity to run a search if it is the last element of a large array.
There is a faster way, Array#bsearch
can find an element with only O (log n) complexity.
Here is the difference in search time between the two methods when searching within 50,000,000 numbers:
1 2 3 4 5 6 7 8 9 10 11 12 13 | require 'benchmark' data = (0..50_000_000) Benchmark.bm do |x| x.report(:find) { data.find {|number| number > 40_000_000 } } x.report(:bsearch) { data.bsearch {|number| number > 40_000_000 } } end user system total real find 2.257520 0.000000 2.257520 ( 2.257550) bsearch 0.000006 0.000000 0.000006 ( 0.000006) |
As you can see, bsearch
much faster. However, when using bsearch
: The array must be sorted, which somewhat limits its usefulness.
Note: The complexity of the search algorithms is sorted in order: O (1), O (log n), O (n), O (n log (n)), O (n ^ 2) , O (2 ^ n), O (n!), Where n is the size of the array. You can find out more details via Google
9. Enumerable#flat_map
Returns a new array with the results of being executed in the block once for all elements in the enum. If there is no block, an enumerator is returned.
When dealing with relational data, sometimes we need to collect a bunch of extraneous properties and return them in a non-nested array.
Let’s say you have a blog and you want to find the author of the comments left on posts written in the last month by a certain group of users:
1 2 3 4 5 6 7 8 9 10 11 12 13 | class Comment < ApplicationRecord def self.find_users(user_ids) users = User.where(id: user_ids) users.map { |user| user.posts.map { |post| post.comments.map { |comment| comment.author.name }.flatten }.flatten }.flatten end end |
You will then get a similar link:
1 2 3 | Comment.find_users [1,2,3,4,5,6,7,8,9] => ["Author 1", "Author 2", "Author 3", "Author 4", nil, "Author 5"] |
You have another option to obtain the same result:
1 2 3 4 5 6 7 8 9 10 11 12 13 | class Comment < ApplicationRecord def self.find_users(user_ids) users = User.where(id: user_ids) users.flat_map { |user| user.posts.flat_map { |post| post.comments.flat_map { |comment| comment.author.name } } } end end |
10. <=>
To illustrate how <=>
works, let’s experiment with:
5<=>5
, returns 0.4<=>5
, returns -1.5<=>4
, returns 1.
=> If two numbers are equal, 0 is returned; range, in ascending order, returns -1; variable, in descending order, returns 1; If the comparison is not possible, nil is returned
<=>
is useful for specifying sort order and arithmetic operations because it returns one of a value of type Fixnum.
Hope the methods introduced above are useful to you!
Thank you for reading!
Refer
7 Little-Known Ruby Methods To Help You Write Better Code