Question
In Ruby, how can you transform each element in an array with a method that may return either a new value or nil, and then remove the nil results in an idiomatic way?
For example, suppose you have a method like this:
def transform(n)
rand > 0.5 ? n * 10 : nil
end
And you are currently doing this in two steps:
items.map! { |x| transform(x) } # [1, 2, 3, 4, 5] => [10, nil, 30, 40, nil]
items.reject! { |x| x.nil? } # [10, nil, 30, 40, nil] => [10, 30, 40]
You could also build a new array manually:
new_items = []
items.each do |x|
x = transform(x)
new_items << x unless x.nil?
end
items = new_items
However, that feels less idiomatic. Is there a cleaner Ruby pattern for mapping over a list while excluding nil values as part of the same operation?
Short Answer
By the end of this page, you will understand how to transform array elements in Ruby and remove nil results cleanly. You will learn when to use map + compact, filter_map, in-place methods like map! and compact!, and how these patterns appear in real Ruby code.
Concept
In Ruby, map is used to transform each element of a collection into a new value. But sometimes a transformation may fail, skip an item, or intentionally return nil. When that happens, you often want to keep only the meaningful results.
This is a very common pattern:
- Transform each item
- Discard empty results
In Ruby, nil is often used to mean “no value” or “skip this item”. So if your transformation returns either a real value or nil, your next step is usually to remove the nils.
The most common idiomatic approaches are:
map { ... }.compactmap! { ... }; compact!filter_map { ... }in modern Ruby
Why this matters:
- It keeps your code expressive and readable.
- It avoids manual temporary arrays in many cases.
- It communicates intent clearly: transform values, then keep only valid results.
This pattern appears often in:
- data cleanup
- parsing
- API response processing
- optional lookups
- validation pipelines
A key detail: map always returns an array of the same length as the input. If you want the output to be shorter because some items are skipped, you need an extra step like , or you use , which combines both ideas.
Mental Model
Think of map like sending every item through a machine on a conveyor belt.
- If the machine produces a result, that result goes into the output tray.
- If the machine produces
nil, the tray still gets a placeholder unless you clean it up.
So:
map= process every itemcompact= throw away empty placeholders (nil)filter_map= process the item and only keep real results in one pass
Analogy:
mapis like stamping every form.nilmeans the form was rejected.compactremoves rejected forms afterward.filter_maponly files accepted forms in the first place.
Syntax and Examples
Core syntax
array.map { |item| transform(item) }.compact
This transforms every item, then removes all nil values.
Example
def transform(n)
n.even? ? n * 10 : nil
end
items = [1, 2, 3, 4, 5]
result = items.map { |x| transform(x) }.compact
p result
# => [20, 40]
What happens here?
1becomesnil2becomes203becomesnil4becomes405becomesnil
Step by Step Execution
Consider this example:
def transform(n)
n > 2 ? n * 10 : nil
end
items = [1, 2, 3, 4]
result = items.map { |x| transform(x) }.compact
Step 1: Start with the original array
items = [1, 2, 3, 4]
Step 2: Run map
Ruby calls the block once for each element.
- For
1:transform(1)returnsnil - For
2:transform(2)returnsnil - For
3:transform(3)returns30 - For : returns
Real World Use Cases
This pattern shows up often when a transformation may or may not produce a useful value.
1. Parsing optional data
strings = ["10", "abc", "25"]
numbers = strings.map { |s| Integer(s, exception: false) }.compact
# => [10, 25]
Invalid numbers become nil, then get removed.
2. Looking up database records
user_ids = [1, 2, 999]
users = user_ids.map { |id| User.find_by(id: id) }.compact
Missing records return nil, and you keep only found users.
3. Building API payloads
fields = [:name, :email, :phone]
payload = fields.map { |field| value = user[field]; [field, value] if value }.compact.to_h
Only fields with values are included.
4. Cleaning imported data
Real Codebase Usage
In real Ruby projects, developers use this concept in a few common ways.
map + compact for clarity
This is very readable and works well when you explicitly want to remove only nil values.
emails = users.map { |user| user.email if user.active? }.compact
filter_map for combined transform-and-filter logic
This is common in modern Ruby because it is concise.
emails = users.filter_map { |user| user.email if user.active? }
Guard clauses inside the block
Developers often return early from the block when an item should be skipped.
results = records.filter_map do |record|
next unless record.valid?
record.id
end
Validation pipelines
A method may return a processed value or nil when input is invalid.
def normalize_phone()
digits = phone.gsub(, )
digits.length ==
digits
phones = raw_phones.map { || normalize_phone(p) }.compact
Common Mistakes
1. Using filter_map when false is a valid value
filter_map removes all falsey values, including false.
values = [1, 2, 3]
result = values.filter_map { |n| n == 2 ? false : n }
# => [1, 3]
If false should stay, use:
result = values.map { |n| n == 2 ? false : n }.compact
# => [1, false, 3]
2. Forgetting that map keeps array length the same
Broken expectation:
result = [1, 2, 3].map { |n| n if n.even? }
# => [nil, 2, nil]
map transforms every element, but it does not remove elements.
3. Assuming always returns an array
Comparisons
| Approach | Keeps original array? | Removes only nil? | Removes false too? | Best use |
|---|---|---|---|---|
map { ... }.compact | Yes | Yes | No | Clear and explicit |
map! { ... }; compact! | No | Yes | No | In-place mutation |
filter_map { ... } | Yes | No | Yes | Concise transform + filter |
each_with_object([]) | Yes | Custom |
Cheat Sheet
Quick reference
Transform then remove nil
result = items.map { |x| transform(x) }.compact
In-place version
items.map! { |x| transform(x) }
items.compact!
Modern Ruby
result = items.filter_map { |x| transform(x) }
Rules
maptransforms every element.mapdoes not remove elements.compactremovesnilvalues only.compact!mutates the array and may returnnilif unchanged.filter_maptransforms and keeps only truthy results.filter_mapremoves bothnilandfalse.
Choose this when
FAQ
Is map.compact the idiomatic Ruby solution?
Yes. It is a common and readable way to transform values and then remove nil results.
What is the difference between compact and reject(&:nil?)?
They can produce the same result for removing nil, but compact is shorter and clearer when nil is the only thing you want to remove.
Should I use filter_map instead of map.compact?
Use filter_map if you want a concise combined operation and you are okay with removing both nil and false values.
Does map! change the original array?
Yes. The bang version mutates the existing array.
Does compact! always return the modified array?
No. It returns nil if no nil values were removed.
Mini Project
Description
Build a small Ruby script that processes a list of user-entered strings. Some strings should be converted into integers, while invalid inputs should be skipped. This demonstrates the common pattern of transforming values and removing nil results cleanly.
Goal
Create a script that converts valid numeric strings into integers and ignores invalid entries using idiomatic Ruby collection methods.
Requirements
- Start with an array of strings containing valid and invalid numbers.
- Write a method that returns an integer for valid input and
nilfor invalid input. - Produce a final array containing only valid converted integers.
- Show both a
map + compactsolution and afilter_mapsolution. - Print the final cleaned array.
Keep learning
Related questions
Calling a Class Method from an Instance in Ruby
Learn how to call a class method from an instance in Ruby using self.class, with examples, pitfalls, and practical usage patterns.
Calling an Overridden Monkey-Patched Method in Ruby
Learn how to call the original method when monkey patching in Ruby, including alias_method patterns, examples, pitfalls, and practical usage.
Convert a Unix Timestamp to Ruby DateTime
Learn how to convert Unix timestamps to Ruby DateTime and Time objects, with examples, differences, pitfalls, and practical Ruby usage.