Tuesday, 15 November 2016

How to map and remove nil values in Ruby



I have a map which either changes a value or sets it to nil. I then want to remove the nil entries from the list. The list doesn't need to be kept.



This is what I currently have:



# A simple example function, which returns a value or nil
def transform(n)
rand > 0.5 ? n * 10 : nil }
end

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]


I'm aware I could just do a loop and conditionally collect in another array like this:



new_items = []
items.each do |x|
x = transform(x)
new_items.append(x) unless x.nil?
end
items = new_items


But it doesn't seem that idiomatic. Is there a nice way to map a function over a list, removing/excluding the nils as you go?


Answer



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]

No comments:

Post a Comment

c++ - Does curly brackets matter for empty constructor?

Those brackets declare an empty, inline constructor. In that case, with them, the constructor does exist, it merely does nothing more than t...