Hash.filter_map / flat_map / reduce
| Since: | Ruby 2.7(2019) |
|---|
Covers filter_map for simultaneous filtering and transformation on hashes, flat_map for flattening nested results, and the aggregation methods reduce and inject.
Syntax
hash.filter_map { |key, value| transformed_value or nil/false }
# Returns an array with block results flattened one level.
hash.flat_map { |key, value| array }
# Aggregates all pairs into a single value.
hash.reduce(initial) { |accumulator, (key, value)| ... }
hash.inject(initial) { |accumulator, (key, value)| ... }
# Returns a hash with pairs grouped by the block's return value.
hash.group_by { |key, value| group_key }
Method List
| Method | Description |
|---|---|
| filter_map { |k, v| ... } | Includes an element in the result array only when the block returns a value other than nil or false. |
| flat_map { |k, v| ... } | Concatenates the arrays returned by the block into a single array. When the block does not return an array, it behaves the same as map. |
| reduce(init) { |acc, (k, v)| ... } | Starts from an initial value, accumulates each pair, and returns the final result. inject is an alias. |
| min_by { |k, v| ... } | Returns the pair with the smallest block return value as [key, value]. |
| max_by { |k, v| ... } | Returns the pair with the largest block return value. |
| sort_by { |k, v| ... } | Returns an array sorted by the block's return value. |
| group_by { |k, v| ... } | Returns a hash with pairs grouped by the block's return value. |
Sample Code
sample_hash_filter_map_flat_map.rb
enforcers = { kogami: 5, ginoza: 0, masaoka: 8, karanomori: 0, tsunemori: 3 }
# filter_map: get only active member names (select + map in one pass)
active = enforcers.filter_map { |member, score| member.to_s if score > 0 }
puts active.inspect # ["kogami", "masaoka", "tsunemori"]
# flat_map: expand each pair into multiple strings
labels = enforcers.flat_map { |member, score| [member.to_s, score.to_s] }
puts labels.inspect
# ["kogami", "5", "ginoza", "0", "masaoka", "8", "karanomori", "0", "tsunemori", "3"]
# reduce: calculate the total score
total = enforcers.reduce(0) { |sum, (_, score)| sum + score }
puts "Total score: #{total}" # Total score: 16
# reduce: build a hash of active members only
active_hash = enforcers.reduce({}) do |hash, (member, score)|
hash[member] = score if score > 0
hash
end
puts active_hash.inspect # {:kogami=>5, :masaoka=>8, :tsunemori=>3}
# min_by / max_by: find the member with the least and most score
puts enforcers.min_by { |_, score| score }.inspect # [:ginoza, 0]
puts enforcers.max_by { |_, score| score }.inspect # [:masaoka, 8]
# sort_by: sort by score
sorted = enforcers.sort_by { |_, score| score }
sorted.each { |member, score| puts "#{member}: #{score}" }
Running the code produces the following output:
ruby hash_filter_map_flat_map.rb
["kogami", "masaoka", "tsunemori"]
["kogami", "5", "ginoza", "0", "masaoka", "8", "karanomori", "0", "tsunemori", "3"]
Total score: 16
{:kogami=>5, :masaoka=>8, :tsunemori=>3}
[:ginoza, 0]
[:masaoka, 8]
ginoza: 0
karanomori: 0
tsunemori: 3
kogami: 5
masaoka: 8
Notes
filter_map was added in Ruby 2.7. It performs filtering and transformation in a single iteration. It combines the two-step select { }.map { } pattern into one, improving performance and reducing code length.
reduce (also inject) is powerful but can hurt readability. When you only need a sum, minimum, or maximum, prefer the more expressive sum, min_by, or max_by. For building a hash, each_with_object is often easier to read.
Using sort_by on a hash returns an array of pairs. To convert the result back to a hash, append to_h at the end. For multi-criteria sorting, return an array from the block (e.g., sort_by { |k, v| [v, k.to_s] }).
If you find any errors or copyright issues, please contact us.