DEV Community

Junko T.
Junko T.

Posted on

Ruby iteration shortcut with &(ampersand) operator

Ruby logo

One of the reasons why Ruby is my favorite language is that it has many shortcuts and can do a lot with a few lines of code. & (ampersand) operator is one great example.

A quick explanation of how & works in Ruby:

# These two calls are equivalent:

numbers.select { |number| number.even? }

numbers.select(&:even?)
Enter fullscreen mode Exit fullscreen mode

& calls the to_proc method on the following object which is a symbol :even? in the example above. (the to_proc method is implemented on symbols.) This means the object will be encapsulated as a block of code (as a Proc object), and then passed to the given method.

This might still look weird to you as it did to me in the beginning because .even? is a built-in method in Ruby. Why can I use it as a symbol? This works because method names in Ruby are internally stored as symbols. For the same reason, you can use this & shortcut for the methods you define as well.

Let's see the examples with Ruby Enumerable that you use everyday:

1. find

# Returns the first admin user

# BAD
def find_admin(users)
  users.find { |user| user.admin? }
end

# GOOD
def find_admin(users)
  users.find(&:admin?)
end
Enter fullscreen mode Exit fullscreen mode

2. select

# Returns all the admin users

# BAD
def find_admins(users)
  admins = []
  users.each do |user|
    admins << user if user.admin?
  end
  admins
end

# GOOD
def find_admins(users)
  users.select(&:admin?)
end
Enter fullscreen mode Exit fullscreen mode

3. count

# Returns the number of the admin users

# BAD
def count_admin(users)
  count = 0
  users.each do |user|
    count += 1 if user.admin?
  end
  count
end

# GOOD
def count_admin(users)
  users.count(&:admin?)
end
Enter fullscreen mode Exit fullscreen mode

4. map

# Returns an array of each user's name

# BAD
def user_names(users)
  names = []
  users.each do |user|
    names << user.name
  end
  names
end

# GOOD
def user_names(users)
  users.map(&:name)
end
Enter fullscreen mode Exit fullscreen mode

5. any?

# Returns true if at least one user exists.

# BAD
def contains_nil?(users)
  users.each do |user|
    return true if user.nil?
  end
  false
end

# GOOD
def contains_nil?(users)
  users.any?(&:nil?)
end
Enter fullscreen mode Exit fullscreen mode

6. max/max_by

# Returns the oldest user.

# BAD
def oldest_user(users)
  oldest = nil
  users.each do |user|
    oldest = user if oldest.nil? || user.age > oldest.age
  end
  oldet
end

# GOOD
def oldest_user(users)
  users.max_by(&:age)
end
Enter fullscreen mode Exit fullscreen mode

Knowing this, when you iterate over an array using a block where you simply return object.method, you will notice code smells and consider using & operator instead.

Top comments (2)

Collapse
 
sabarishcodes profile image
Sabarish Rajamohan

Nice list of methods to use !!! Thank you

Collapse
 
junko911 profile image
Junko T.

Thank you for reading!