How to map and remove nil values in Ruby
You could use compact
:
[1, nil, 3, nil, nil].compact
=> [1, 3]
I'd like to remind people that if you're getting an array containing nils as the output of a map
block, and that block tries to conditionally return values, then you've got code smell and need to rethink your logic.
For instance, if you're doing something that does this:
[1,2,3].map{ |i|
if i % 2 == 0
i
end
}
# => [nil, 2, nil]
Then don't. Instead, prior to the map
, reject
the stuff you don't want or select
what you do want:
[1,2,3].select{ |i| i % 2 == 0 }.map{ |i|
i
}
# => [2]
I consider using compact
to clean up a mess as a last-ditch effort to get rid of things we didn't handle correctly, usually because we didn't know what was coming at us. We should always know what sort of data is being thrown around in our program; Unexpected/unknown data is bad. Anytime I see nils in an array I'm working on, I dig into why they exist, and see if I can improve the code generating the array, rather than allow Ruby to waste time and memory generating nils then sifting through the array to remove them later.
'Just my $%0.2f.' % [2.to_f/100]
Try using reduce
or inject
.
[1, 2, 3].reduce([]) { |memo, i|
if i % 2 == 0
memo << i
end
memo
}
I agree with the accepted answer that we shouldn't map
and compact
, but not for the same reasons.
I feel deep inside that map
then compact
is equivalent to select
then map
. Consider: map
is a one-to-one function. If you are mapping from some set of values, and you map
, then you want one value in the output set for each value in the input set. If you are having to select
before-hand, then you probably don't want a map
on the set. If you are having to select
afterwards (or compact
) then you probably don't want a map
on the set. In either case you are iterating twice over the entire set, when a reduce
only needs to go once.
Also, in English, you are trying to "reduce a set of integers into a set of even integers".