Hash#filter_map
Hash#filter_map is not a Hash-specific method — it’s Enumerable#filter_map, which Hash inherits. It arrived in Ruby 2.7 and does what map { }.compact or select { }.map { } do in two passes, but in one. Ruby passes each key-value pair as a two-element array [key, value] when iterating a hash.
That makes it a good fit for hashes where you want to drop unwanted entries and transform the survivors at the same time. Instead of creating an intermediate array and then cleaning it up, filter_map lets you keep the important values in a single pass.
Signature
hash.filter_map { |key, value| block }
Returns an array containing the truthy (non-nil/false) return values from the block.
No block given? Returns an Enumerator. That means you can delay the computation or chain the Enumerator with other iteration methods later.
How it works
The block is called once per entry. Whatever the block returns gets kept only if it’s truthy. Compare:
# filter then map — two passes
{ a: 1, b: 2, c: 3 }.select { |_k, v| v.even? }.map { |_k, v| v * 10 }
# => [20]
# filter_map — one pass
{ a: 1, b: 2, c: 3 }.filter_map { |_k, v| v.even? ? v * 10 : nil }
# => [20]
The nil returned for odd values is dropped. You can also explicitly return false to filter — both are treated as falsy. That combined behavior makes filter_map a natural fit when the data needs both filtering and reshaping in the same pass. The method is equally comfortable with hashes because the block destructures each entry into separate key and value parameters, giving you direct access to both sides of the pair without extra bookkeeping.
Hash Example
Given a config hash where you want only the keys whose values are strings, transformed to uppercase:
config = { host: "localhost", port: 5432, ssl: true, db: "production" }
config.filter_map { |key, value|
value.is_a?(String) ? [key, value.upcase] : nil
}
# => [[:db, "PRODUCTION"]]
The output is an array of two-element arrays because filter_map always produces an array regardless of what the block yields — it never returns a hash directly. If downstream code expects a proper hash instead of an array of pairs, chaining to_h at the end consolidates the pairs into the expected type:
config.filter_map { |key, value|
value.is_a?(String) ? [key, value.upcase] : nil
}.to_h
# => { db: "PRODUCTION" }
With symbolized keys
filter_map is especially useful when your data has string keys from an external source and you want both symbol-keyed results and filtered values. This pattern crops up frequently with API responses and database result sets where the key format is not something you control:
api_response = {
"user_id" => 42,
"email" => "alice@example.com",
"active" => true,
"created_at" => "2024-01-15"
}
api_response.filter_map { |key, value|
next unless value.is_a?(String) && !value.empty?
[key.to_sym, value]
}.to_h
# => { email: "alice@example.com", created_at: "2024-01-15" }
next skips entries without an explicit return value — same as returning nil. That makes the method flexible: you can either pass a block to run the computation immediately, or capture the Enumerator and build a lazy pipeline that triggers later. Chaining with other Enumerable methods after the Enumerator gives you access to the full collection toolkit without forcing an eager intermediate result.
Enumerator Form
Without a block, filter_map returns an Enumerator so you can chain it:
enum = { a: 1, b: 2, c: 3 }.filter_map
enum.map { |_k, v| v * 2 if v.odd? }
# => [2, nil, 6] — compact it after if you need only truthy results
For hashes specifically, chaining to to_h is a common pattern.
Performance
filter_map iterates once. select { }.map { } iterates twice. On large collections the difference is measurable. The Ruby core team benchmarked it and found filter_map faster than both the two-pass approach and reduce for combined filter-map workloads.
require "benchmark"
hash = (1..100_000).to_h { |i| [i, i] }
Benchmark.measure do
100.times { hash.select { |k, _| k.even? }.map { |_, v| v * 2 } }
end
Benchmark.measure do
100.times { hash.filter_map { |k, v| v * 2 if k.even? } }
end
The filter_map version consistently comes out faster.
Compared to other methods
| Method | Iterations | Result |
|---|---|---|
select + map | 2 | Array |
map + compact | 2 | Array |
filter_map | 1 | Array |
collect + reject | 2 | Array |
filter_map is the only one that does the job in a single pass.
See Also
- /reference/hash-methods/filter/ —
Hash#filter(same asselect) - /reference/hash-methods/flat-map/ —
Hash#flat_mapfrom Enumerable - /reference/enumerable/enumerable-filter-map/ — base Enumerable#filter_map