Ruby 3.1 adds Enumerable#compact and Enumerator::Lazy#compact

Ashik Salman

By Ashik Salman

on April 6, 2021

This blog is part of our  Ruby 3.1 series.

We are familiar with the compact method associated with arrays. The compact method returns a copy of the array after removing all nil elements.

Ruby 3.1 introduces the compact method in the Enumerable module. Now we can use the compact method along with the Enumerator and Enumerator::Lazy classes which include the Enumerable module.

Before Ruby 3.1

1=> enum = [1, nil, 3, nil, 5].to_enum
2=> #<Enumerator: ...>
3
4=> enum.compact
5=> NoMethodError (undefined method `compact' for #<Enumerator: [1, nil, 3, nil, 5]:each>)
6
7=>  enum.reject { |x| x.nil? }
8=> [1, 3, 5]

After Ruby 3.1

1=> enum = [1, nil, 3, nil, 5].to_enum
2=> #<Enumerator: ...>
3
4=> enum.compact
5=> [1, 3, 5]

We can access the compact method to remove all nil occurrences from any classes where we include the Enumerable module.

1class Person
2  include Enumerable
3
4  attr_accessor :names
5
6  def initialize(names = [])
7    @names = names
8  end
9
10  def each &block
11    @names.each(&block)
12  end
13end
14
15=> list = Person.new(["John", nil, "James", nil])
16=> #<Person:0x0000000101cd3de8 @names=["John", nil, "James", nil]>
17
18=> list.compact
19=> ["John", "James"]

Similarly, lazy evaluation can be chained with the compact method to remove all nil entries from the Enumerator collection.

1=> enum = [1, nil, 3, nil, 5].to_enum.lazy.compact
2=> #<Enumerator::Lazy: ...>
3
4=> enum.force
5=> [1, 3, 5]
6
7
8=> list = Person.new(["John", nil, "James", nil]).lazy.compact
9=> #<Enumerator::Lazy: ...>
10
11=> list.force
12=> ["John", "James"]

Here's the relevant pull request and feature discussion for this change.