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.