Enumerable#filter_map

Returns: Array · Added in v2.7 · Updated March 16, 2026 · Enumerable
ruby enumerable filter transform

The filter_map method filters elements with a block and transforms them in a single pass. It’s equivalent to select.map but more efficient since it only iterates once.

How It Works

filter_map combines two operations:

  1. Filter — keep elements where the block returns truthy
  2. Map — transform the kept elements
collection.filter_map { |element| block_return }
# Equivalent to: collection.select { |x| block_return }.map { |x| block_return }
# But more efficient — single iteration

Basic Usage

Filter and transform in one step:

numbers = [1, 2, 3, 4, 5, 6]

# Keep only even numbers and double them
result = numbers.filter_map { |n| n.even? ? n * 2 : nil }
# => [4, 8, 12]

Remove nil values while transforming:

values = ["1", "two", "3", "four", "5"]

# Convert to integers, discarding non-numeric strings
result = values.filter_map { |s| Integer(s) rescue nil }
# => [1, 3, 5]

Filtering Strings

Process and filter strings:

words = ["hello", "", "world", " ", "ruby", nil]

# Keep non-empty strings, upcase them
result = words.filter_map { |w| w&.strip&.upcase if w && !w.empty? }
# => ["HELLO", "WORLD", "RUBY"]

Extract and transform data from hashes:

users = [
  { name: "Alice", email: nil },
  { name: "Bob", email: "bob@example.com" },
  { name: "Carol", email: "carol@example.com" }
]

emails = users.filter_map { |u| u[:email] }
# => ["bob@example.com", "carol@example.com"]

With Conditional Logic

Generate results only for elements that meet a condition:

orders = [
  { id: 1, total: 150, status: "pending" },
  { id: 2, total: 50, status: "completed" },
  { id: 3, total: 200, status: "completed" },
  { id: 4, total: 75, status: "pending" }
]

# Get IDs of completed orders over $100
order_ids = orders.filter_map { |o| o[:id] if o[:status] == "completed" && o[:total] > 100 }
# => [3]

Extract nested values:

data = [
  { user: { profile: { name: "Alice" } } },
  { user: nil },
  { user: { profile: { name: "Bob" } } },
  { user: { profile: nil } }
]

names = data.filter_map { |d| d.dig(:user, :profile, :name) }
# => ["Alice", "Bob"]
MethodBehavior
filter_mapKeeps truthy results, discards nil/false
mapReturns transformed result for every element
selectKeeps all elements, returns same type
collectAlias for map
numbers = [1, 2, 3, 4, 5]

numbers.map { |n| n * 2 }          # => [2, 4, 6, 8, 10]
numbers.select { |n| n.even? }    # => [2, 4]
numbers.filter_map { |n| n * 2 if n.even? }  # => [4, 8]

Performance Benefits

filter_map is more efficient than chaining select and map:

# Two iterations
result = numbers.select { |n| n.even? }.map { |n| n * 2 }

# One iteration
result = numbers.filter_map { |n| n.even? ? n * 2 : nil }

This matters for large datasets or expensive operations in the block.

Practical Examples

Parsing and Filtering Data

strings = ["42", "abc", "99", "hello", "7"]

# Convert to integers, keep only positive
nums = strings.filter_map { |s| Integer(s) if Integer(s) > 0 rescue nil }
# => [42, 99, 7]

Extracting Valid Records

records = [
  { name: "Alice", age: 30, email: "alice@example.com" },
  { name: "Bob", age: nil, email: "bob@example.com" },
  { name: "Carol", age: 25, email: nil },
  { name: "Dave", age: 35, email: "dave@example.com" }
]

# Get names of people with both age and email
valid_names = records.filter_map { |r| r[:name] if r[:age] && r[:email] }
# => ["Alice", "Dave"]

Processing API Responses

responses = [
  { status: 200, data: { users: [] } },
  { status: 404, data: nil },
  { status: 500, data: nil },
  { status: 200, data: { items: [] } }
]

# Extract successful response data
successful_data = responses.filter_map { |r| r[:data] if r[:status] == 200 }

Return Value

Always returns an array:

[].filter_map { |x| x }              # => []

[1, 2, 3].filter_map { |n| n if n.even? }
# => [2]

Edge Cases

Block returns false (not nil):

[1, 2, 3].filter_map { |n| false }
# => [false]

All elements filtered out:

[1, 2, 3].filter_map { |n| nil }
# => []

Mixed nil and false:

[1, 2, 3].filter_map { |n| n == 2 ? nil : n }
# => [1, 3]

[1, 2, 3].filter_map { |n| n == 2 ? false : n }
# => [1, false, 3]

See Also