rubyguides

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.

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.

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.

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 — use to_h if you need a hash back:

config.filter_map { |key, value|
  value.is_a?(String) ? [key, value.upcase] : nil
}.to_h
# => { db: "PRODUCTION" }

With Symbolized Keys

Often you have a raw API response hash with string keys and want to extract and transform specific fields:

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.

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

MethodIterationsResult
select + map2Array
map + compact2Array
filter_map1Array
collect + reject2Array

filter_map is the only one that does the job in a single pass.

See Also