benizi
benizi

Reputation: 4216

Idiomatic Ruby filter for nil-or-empty?

I'm looking for a more idiomatic way to filter out nil-or-empty elements of an array.

I have many methods of the form:

def joined
    [some_method, some_other_method].compact.reject(&:empty?).join(' - ')
end

This will take the result of some_method and some_other_method and return only the one(s) that are both non-nil (compact is essentially equivalent to reject(&:nil?)) and non-empty.

Is there anything in Array or Enumerable that gets the same thing in one shot?

Upvotes: 23

Views: 14560

Answers (7)

aks
aks

Reputation: 2348

ActiveSupport, part of Rails, has this method, and so does the blank_empty_nil_filters gem: is_blank? and is_empty?.

There is a distinction between the two. Some use-cases tolerate blanks (whitespace), some do not.

Perhaps most importantly, filtering out these kinds of values from Hash and Array values is already built-in to blank_empty_nil_filters. Examples:

array.no_nil_values
array.no_empty_values
array.no_blank_values

See the README

ActiveSupport a much larger, more comprehensive gem, uses Regexp.match to determine blankness, while the blank_empty_nil_filters gem code uses a non-destructive strip, which is both faster and less sensitive to non-UTF8 string error conditions.

Upvotes: 0

imtk
imtk

Reputation: 1548

I think you don't need to use reject method. Simply use Array's select method.

def joined
  [some_method, some_other_method].select { |method| method.present? }
end

Or even better:

def joined
  [some_method, some_other_method].select(&:present?)
end

For more Idiomatic way to write Ruby code, you should read this article

Upvotes: 0

nickl-
nickl-

Reputation: 8731

@trans enjoyed my +1 but after further deliberation I've reached the following conclusion.

If we go on the premise that everything is an Object then all we actually need is a patch to class Object

class Object
    def empty?
        self == 0 or not self
    end
end

Which satisfies my requirement:

1.9.3 :001 > d=[Object.new, Class, {a:nil}, 'a', '', [], 1, 1.1, 0, 0.0, 0x0, 0E0, true, false, nil]
 => [#<Object:0x007fd56c086918>, Class, {:a=>nil}, "a", "", [], 1, 1.1, 0, 0.0, 0, 0.0, true, false, nil] 
1.9.3 :002 > d.reject(&:empty?)
 => [#<Object:0x007fd56c086918>, Class, {:a=>nil}, "a", 1, 1.1, true] 

What about yours? Do we need something more? Please state your qualms or show silent agreement by voting up to help motivate a language change upstream.

nJoy!

Upvotes: 0

trans
trans

Reputation: 1441

Perhaps we need but one simple extension to NilClass?

class NilClass
  def empty?
    true
  end
end

Upvotes: 2

user904990
user904990

Reputation:

monkeypatches accepted? :)

you can try this:

class Array
  def tight
    self.compact.reject { |i| i.size.zero? }
  end
end

p [nil, 1, ''].tight
#=> [1]
p ['', nil, 2].tight
#=> [2]

it will work with any objects that responds to size, not only with ones that respond to empty?

Upvotes: 2

Eric
Eric

Reputation: 2884

The following code should do the trick:

[some_method, some_other_method].reject{|i| i.nil? || i.empty? }

It could be easily used to extend the array class:

class Array

  def purge
    self.reject{|i| i.nil? || i.empty? }
  end 

end

And then you can just do:

[some_method, some_other_method].purge

Upvotes: 3

Alex D
Alex D

Reputation: 30445

In Rails, you can do reject(&:blank?), or equivalently, select(&:present?).

If this is not for a Rails app, and you do this a lot, I'd advise you to define your own helper on String or whatever else you are filtering.

class String
  alias :blank? :empty?
end

class NilClass
  def blank?
    true
  end
end

Upvotes: 23

Related Questions