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:
- Filter — keep elements where the block returns truthy
- 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"]
Comparison with Related Methods
| Method | Behavior |
|---|---|
filter_map | Keeps truthy results, discards nil/false |
map | Returns transformed result for every element |
select | Keeps all elements, returns same type |
collect | Alias 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
Enumerable#flat_map— transform and flatten in one passEnumerable#map— transform all elementsEnumerable#select— filter elements by conditionArray#compact— remove nil values from arraysEnumerable#reject— exclude elements by condition