How can I tell if an array is either empty or nil?
Without Rails or ActiveSupport,
array.to_a.empty?
There's no built-in Ruby method that does this, but ActiveSupport's blank does:
>> require "active_support/core_ext/object/blank" #=> true
>> nil.blank? #=> true
>> [].blank? #=> true
You can just use the Array#empty? and Object#nil? methods in conjunction with an OR.
arr.nil? || arr.empty?
This will return true of array is empty or the array value is nil.
To check whether array is empty one can use 'empty?' inbuilt method as follows,
array.empty? # returns true/false
To check whether array is nil (If not initialized or set to nil)
array.nil? # returns true/false
Related
ActiveSupport extends Object with an instance method blank?:
class Object
def blank?
respond_to?(:empty?) ? !!empty? : !self
end
end
Could !!empty? be written as empty? instead? Is this a stylistic choice so that it reads easily as a method returning a boolean? Or is there something else?
The reason for this is that !! coerces the response from empty to a boolean. Empty can be defined differently on different objects so it is possible that someone in rails could have defined .empty? to not return a boolean. Since .blank? needs to return a boolean the !! is needed to ensure that a boolean is returned.
It is a common way to convert a truthy versus falesy value into strict true and false.
It is a common approach in Ruby to call !!(something). The result of the calculation will be boolean, not nil or something else:
!!(true) # true
!!(false) # false
!!(nil) # false
!! is used to force falsey/truthy values to false/true:
irb(main):001:0> !!nil == false
=> true
In fact, it used to be empty?. Here's the commit that changed it to !!empty?: https://github.com/rails/rails/commit/126dc47665c65cd129967cbd8a5926dddd0aa514
From the comments:
Bartuz:
Why double !! ? It returns the TrueClass / FalseClass anynway
fxn:
Because it is a dynamic language and subject to polymorphism, you just can't rely on empty? returning singletons, what you need to guarantee is that you return one no matter what.
The "improved" implementation however is incomplete, because you could just as well implement ! to return a non-boolean value:
class Foo
def !
nil
end
end
Foo.new.blank? #=> nil
To handle both methods (empty? and !), it should be implemented as:
!!(respond_to?(:empty?) ? empty? : !self)
Defining a method doesn't seem to evaluate to a truthy value as can be checked by putting one inside an if condition:
if(def some_method; puts "random text"; end) then
puts "declaration evaluates to true"
else
puts "declaration evaluates to false"
end
# => declaration evaluates to false
Why/How does a method declaration evaluate to nil?
It actually evaluates to nil. This makes sense; why would a method creation return anything?
irb(main):001:0> def test; print 'test'; end
=> nil
However, it has to return something, so to return "nothing" would be to return nil.
Every statement in Ruby evaluates to something. The def statement's value is not supposed to be checked and is therefore nil.
You will find the behavior you are looking for in the reflective "meta-programming" method define_method.
class EmptyClass
m = define_method(:newmethod) {p "I am the new method"}
p m # => <Proc:0x50b3f359#E:\NetBeansProjects\RubyApplication1\lib\main.rb:6>
end
From Ruby gotchas:
Boolean evaluation of non-boolean data is strict: 0, "" and [] are all evaluated to true. In C, the expression 0 ? 1 : 0 evaluates to 0 (i.e. false). In Ruby, however, it yields 1, as all numbers evaluate to true; only nil and false evaluate to false. A corollary to this rule is that Ruby methods by convention — for example, regular-expression searches — return numbers, strings, lists, or other non-false values on success, but nil on failure. This convention is also used in Smalltalk, where only the special objects true and false can be used in a boolean expression.
Method definions such as def some_method; puts "random text"; end always return nil.
Now, that means the method is evaluated to nil. According to the Ruby Documentation:
Returns false if obj is nil or false; true otherwise.
Since your method return nil, if will evaluate it as false therefore execute the else statement.
i have method, which returns either an array (if it contains any elements) or false if it is nil:
def check_for_four
#four = []
check_values.each do |key, value| ###check_values return hash with key and values as numbers
#four << key if value == 4
end
if #four == nil
return false
else
return #four
end
end
but later on, if i call a method
if some_object.check_for_four
puts "true"
else
puts "false"
end
it always return true, even if #four array is empty. Why is that?
You are checking for whether the array is nil (i.e. is the singleton instance of NilClass) which is very different to checking whether the array is empty.
To check where the array is empty you can either call empty? or if you actually want to check whether it is not empty, you can also use any?.
You can of course also do things like check that the length/size is zero, but it feels more rubyish to me to ask for the specific thing you are interested in.
An empty array is 'truthy' in Ruby. For example:
puts "foo" if []
will output "foo" but
puts "foo" if nil
won't output anything.
I have a string in Ruby on which I'm calling the strip method to remove the leading and trailing whitespace. e.g.
s = "12345 "
s.strip
However if the string is empty nil I get the following error.
NoMethodError: undefined method `strip' for nil:NilClass
I'm using Ruby 1.9 so whats the easiest way to check if the value is nil before calling the strip method?
Update:
I tried this on an element in an array but got the same problem:
data[2][1][6].nil? ? data[2][1][6] : data[2][1][6].split(":")[1].strip
Ruby 2.3.0 added a safe navigation operator (&.) that checks for nil before calling a method.
s&.strip
If s is nil, this expressions returns nil instead of raising NoMethodError.
You can use method try from ActiveSupport (Rails library)
gem install activesupport
require 'active_support/core_ext/object/try'
s.try(:strip)
or you can use my gem tryit which gives extra facilities:
gem install tryit
s.try { strip }
If you don't mind the extra object being created, either of these work:
"#{s}".strip
s.to_s.strip
Without extra object:
s && s.strip
s.strip if s
I guess the easiest method would be the following:
s.strip if s
I'd opt for a solution where s can never be nil to start with.
You can use the || operator to pass a default value if some_method returns a falsy value:
s = some_method || '' # default to an empty string on falsy return value
s.strip
Or if s is already assigned you can use ||= which does the same thing:
s ||= '' # set s to an empty string if s is falsy
s.strip
Providing default scenario's for the absence of a parameters or variables is a good way to keep your code clean, because you don't have to mix logic with variable checking.
Method which works for me (I know, I should never pollute pristine Object space, but it's so convenient that I will take a risk):
class Object
def unless_nil(default = nil, &block)
nil? ? default : block[self]
end
end
p "123".unless_nil(&:length) #=> 3
p nil.unless_nil("-", &:length) #=> "-"
In your particular case it could be:
data[2][1][6].unless_nil { |x| x.split(":")[1].unless_nil(&:strip) }
ActiveSupport comes with a method for that : try. For example, an_object.try :strip will return nil if an_object is nil, but will proceed otherwise. The syntax is the same as send. Cf active_support_core_extensions.html#try.
If you want to avoid the error that appears in the question:
s.to_s.strip
To complete the options shown here, there is the "Existence Check Shorthand", recommended in the Ruby Style Guide:
Use &&= to preprocess variables that may or may not exist. Using &&= will change the value only if it exists [means, is not nil], removing the need to check its existence with if.
So in your case you would do:
s = "12345 "
s &&= s.strip
Simply put:
s = s.nil? ? s : s.strip
Tl;dr Check if s is nil, then return s, otherwise, strip it.
To check what #some_var is, I am doing a
if #some_var.class.to_s == 'Hash'
I am sure that there is a more elegant way to check if #some_var is a Hash or an Array.
You can just do:
#some_var.class == Hash
or also something like:
#some_var.is_a?(Hash)
It's worth noting that the "is_a?" method is true if the class is anywhere in the objects ancestry tree. for instance:
#some_var.is_a?(Object) # => true
the above is true if #some_var is an instance of a hash or other class that stems from Object. So, if you want a strict match on the class type, using the == or instance_of? method is probably what you're looking for.
First of all, the best answer for the literal question is
Hash === #some_var
But the question really should have been answered by showing how to do duck-typing here.
That depends a bit on what kind of duck you need.
#some_var.respond_to?(:each_pair)
or
#some_var.respond_to?(:has_key?)
or even
#some_var.respond_to?(:to_hash)
may be right depending on the application.
Usually in ruby when you are looking for "type" you are actually wanting the "duck-type" or "does is quack like a duck?". You would see if it responds to a certain method:
#some_var.respond_to?(:each)
You can iterate over #some_var because it responds to :each
If you really want to know the type and if it is Hash or Array then you can do:
["Hash", "Array"].include?(#some_var.class) #=> check both through instance class
#some_var.kind_of?(Hash) #=> to check each at once
#some_var.is_a?(Array) #=> same as kind_of
Hash === #some_var #=> return Boolean
this can also be used with case statement
case #some_var
when Hash
...
when Array
...
end
You can use instance_of?
e.g
#some_var.instance_of?(Hash)
I use this:
#var.respond_to?(:keys)
It works for Hash and ActiveSupport::HashWithIndifferentAccess.
In practice, you will often want to act differently depending on whether a variable is an Array or a Hash, not just mere tell. In this situation, an elegant idiom is the following:
case item
when Array
#do something
when Hash
#do something else
end
Note that you don't call the .class method on item.
If you want to test if an object is strictly or extends a Hash, use:
value = {}
value.is_a?(Hash) || value.is_a?(Array) #=> true
But to make value of Ruby's duck typing, you could do something like:
value = {}
value.respond_to?(:[]) #=> true
It is useful when you only want to access some value using the value[:key] syntax.
Please note that Array.new["key"] will raise a TypeError.
irb(main):005:0> {}.class
=> Hash
irb(main):006:0> [].class
=> Array