6

I want to get delete_if to delete empty strings from an array. With the solution below, the array still contains many empty strings.

products = my_text.split(/\t+/)
products.delete_if {|element| element == " " || "" || element.nil?}

Is there anything missing?

4
  • 3
    products.reject! { |s| s.strip.empty? || s.nil? } Commented Sep 27, 2013 at 0:57
  • 5
    You'll want to change that to products.reject! { |product| product.nil? || product.strip.empty? } otherwise you'll try to strip nil Commented Sep 27, 2013 at 1:11
  • Not sure if I follow your change @Momer. Also nil values are what I'd like to delete as well. oldergod's solution seemed to have worked. Commented Sep 27, 2013 at 1:40
  • 1
    Weird, it should have raised an error. Basically, oldergod's block sent strip to s before evaluating whether or not s was nil. This should have raised NoMethodError: undefined method 'strip' for nil:NilClass. By reversing the order, you can avoid that situation. Commented Sep 27, 2013 at 2:01

6 Answers 6

14

The problem with your code is explained by Ed S.
Otherwise, you can do

products.reject! { |s| s.nil? || s.strip.empty? }

Why do you need to test nil? first? Let's check few lines.

nil.strip
# NoMethodError: undefined method `strip' for nil:NilClass
"   ".strip
# => ""

Now, with a different order, what the code does if the object is a string, and then if it is nil.

"  ".strip || "  ".nil?
# => ""
nil.strip || nil.nil?
# NoMethodError: undefined method `strip' for nil:NilClass
# Oh you don't want that to happen, do you?

This means you don't want to call strip.empty? when your object is nil.
And as you know, when you have a || b, if a is truthy (i.e. not nil nor false), b will never be called.
You test first if the string is nil ; if it is, you don't need to check the right part (so you won't get a undefined method error) and the object will be removed from your products list.

Sign up to request clarification or add additional context in comments.

1 Comment

As of Ruby 2.3, you can use the safe navigation operator to avoid checking for nil: products.reject! { |s| s&.strip&.empty? }
4

As per Rails 6.1, You can use compact_blank, https://apidock.com/rails/v6.1.3.1/ActionController/Parameters/compact_blank

["a", "", " "].compact_blank
=> ["a"]

Comments

2

Well this is wrong:

element == " " || "" || element.nil?

Should be

products = products.delete_if {|element| element == " " || element == "" || element.nil?

Note that you had a || "" || in there. You weren't comparing element to "", you were testing the "truthiness" of "" (which evaluates to true btw, screwing up your empty string check).

This of course assumes your definition of an "empty string" is either nil, " ", or "". What about

"  " 

or even

"         "

?

4 Comments

That certainly got me on the right track! But now as I debug it, that line of code executes multiple times and by the time it's done it deletes the whole array. For some reason the boolean evaluates to true since each string has at least one space in it.
Yeah so you anticipated my problem with your answer edit haha.
@AdamBronfin: String comparison doesn't work that way. It's not a contains, it's a comparison against the value, no more. Use @oldergod's suggestion in the comments.
And my experience thus far has shown me I only am plagued with " ", the "" || nil was just out of paranoia. But I suppose I could just include a regex that identifies some continuation of white space and only space.
1

I'm maybe a bit last here (where were you 6 years ago ?), but here is my suggestion,to handle nil , "" and " "

arr.compact.map(&:strip).reject(&:empty?)

For example: ["", nil, "toto", " "].compact.map(&:strip).reject(&:empty?)

# => ["toto"]

Happy Covid lock-down !

Comments

1

So if you have an array of strings could easily remove empty values using reject function:

Having an array of string like this one:

array = ["", "ABC", "DEF", ... , "", "ZXY"]

If you use reject:

array.reject(&:empty?)

Will output a clean array of string:

=> ["ABC", "DEF", "ZXY"]

Comments

1

String#strip each element then reject empty elements:

arr.map!(&:strip).reject!{|s| s.empty?}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.