Reputation: 297
I have a line of code that iterates over an array and rejects any empty elements:
survey.reject!(&:empty?).map! { |feedback| %(_"#{feedback}"_) }
If the entire array is empty, this works as expected. If one of the elements in the array is not empty I get an error map!
doesn't exist.
This, however, has no problems:
survey.map! { |feedback| %(_"#{feedback}_") }
The reject
function works when the entire array is empty, and map!
version works when the array isn't empty. How can I best consolidate these?
Upvotes: 1
Views: 187
Reputation: 369428
Array#reject!
returns nil
if it didn't change anything. nil
doesn't have a map!
method, so you get a NoMethodError
.
Why are you chaining side-effecting methods anyway? The purpose of a side-effecting method is its side-effect, not its return value. You can just do
survey.reject!(&:empty?)
survey.map! { |feedback| %(_"#{feedback}"_) }
Or, better yet: don't use side-effects. They're evil.
survey = survey.reject(&:empty?).map { |feedback| %(_"#{feedback}"_) }
Note, however, that this does not do the same thing as the above: the above mutates the single object that is referenced by survey
. This mutates the reference survey
itself and makes it point to a different object.
This is cleaner overall, but if there is other code in your system that depends on mutable shared state and side-effects, it will have to be changed, too.
Upvotes: 3
Reputation: 211540
You've got to be careful when using in-place modifiers. As the documentation for reject!
says:
Deletes every element of
self
for which the block evaluates to true, if no changes were made returnsnil
.
Due to that feature, you can't chain these like you can the versions that reliably make copies.
What you can do is do it on two lines:
survey.reject!(&:empty?)
survey.map! { |feedback| %(_"#{feedback}"_) }
Or you can chain-reassign:
survey = survey.reject(&:empty?).map { |feedback| %(_"#{feedback}"_) }
Upvotes: 4