Ruby 2.7 adds Enumerable#filter_map

1 minute read

Ruby 2.7 adds Enumerable#filter_map.

filter_map is a shorthand for filter + map in a single call.

Before

Originally suggested 7 years ago, it was addressing a common idiom to map over an Enumerable, but only include the elements that match a particular filter

enum = 1 . upto ( 1_000 ) enum . map { | i | i + 1 if i . even? }. compact # => [3, 5, 7, 9, 11, 13, ...]

Another common approach is selecting + mapping, which is pretty common, for example in ActiveRecord , when manipulating loaded records:

records = User . limit ( 10 ) records . select { | user | user . is_admin? }. map { | user | " #{ user . id } : #{ user . name } " } # => ["1: Jane", "4: Sam" ...]

It’s also possible to achieve something similar with:

enum = 1 . upto ( 1_000 ) enum . each_with_object ([]) { | i , a | a << i + 1 if i . even? } # => [3, 5, 7, 9, 11, 13, ...]

Enumerable#filter_map

We can now use Enumerable#filter_map in Ruby 2.7 to achieve this:

enum = 1 . upto ( 1_000 ) enum . filter_map { | i | i + 1 if i . even? } # => [3, 5, 7, 9, 11, 13, ...]

As you see, filter_map creates a new array after first filtering desired results, and then maps to get expected Array.

This comes in pretty handy for creating mapped arrays in a simpler way.

It also gives us a bit of a speedup from its previous counter-parts

N = 1_00_000 enum = 1 . upto ( 1_000 ) Benchmark . bmbm do | x | x . report ( "select + map" ) { N . times { enum . select { | i | i . even? }. map { | i | i + 1 }}} x . report ( "map + compact" ) { N . times { enum . map { | i | i + 1 if i . even? }. compact }} x . report ( "filter_map" ) { N . times { enum . filter_map { | i | i + 1 if i . even? }}} end # # Rehearsal ------------------------------------------------- # select + map 8.569651 0.051319 8.620970 ( 8.632449) # map + compact 7.392666 0.133964 7.526630 ( 7.538013) # filter_map 6.923772 0.022314 6.946086 ( 6.956135) # --------------------------------------- total: 23.093686sec # # user system total real # select + map 8.550637 0.033190 8.583827 ( 8.597627) # map + compact 7.263667 0.131180 7.394847 ( 7.405570) # filter_map 6.761388 0.018223 6.779611 ( 6.790559)

As you can see, this is faster with about 10-20% speedup.