What is the difference between "include?" and "match"? - ruby

This is my code:
fred = {
'age' => 63,
'gender' => 'male',
'favorite painters' => ['Monet', 'Constable', 'Da Vinci']
}
fred.delete_if { |k,v| k.match(/[a]/) }
puts fred
The result shows {"gender"=>"male"}.
If I change the code to
fred.delete_if { |k,v| k.include?(/[a]/) }
it won't work.
Can anyone explain why to me?

String#match takes a regex object (or a regex pattern string) as the parameter while String#included? takes a string as the parameter.
You should use:
fred.delete_if { |k,v| k.include?('a') }
For more info, see the document.

.include? returns boolean true/false, and expects a string as input.
.match returns information about the match in the form of MatchData (or nil if nothing was matched), and accepts a string or regular expression.

Everyone is recommending using include? for a literal match. I prefer a different syntax that accomplishes the same thing:
"foo".include?("o") # => true
"foo"["o"] # => "o"
"foo".include?("a") # => false
"foo"["a"] # => nil
In Ruby, anything that is not nil or false is considered true, so, for boolean tests the above tests are equivalent if you get a value or true, or if you get false or nil.
If you absolutely must have a boolean result, use the !! ("not not") trick which nicely converts a "truthy" value to its boolean complement, then back to that value's boolean complement.
true # => true
false # => false
'a' # => "a"
nil # => nil
!true # => false
!false # => true
!'a' # => false
!nil # => true
!!true # => true
!!false # => false
!!'a' # => true
!!nil # => false
Which lets us do:
!!"foo"["o"] # => true
!!"foo"["a"] # => false
This results in more compact code, which might not be what your particular coding-style wants. It pushes the Ruby code toward Perl or C code so weigh the compactness with readability and pick which style you want.
See String#[] for more information.

Related

Ruby Method not Returning False within a Recursion Block

I'm doing a ruby challenge that I found on rubeque.com. Here are the instructions:
Instructions:
Write a method #r_empty? that returns true if a hash and its subhashes are empty or false if there is a value in the hash.
My Answer:
class Hash
def r_empty?
def recurse(h)
h.each {|key, value|
value.is_a?(Hash) ? recurse(value) :
if (value!=nil && value!="")
#puts value
return false
end
}
return true
end
recurse(self)
end
end
Test:
a = {:ruby => "", :queue => ""}
b = {:ruby => {:version => {:one => {"nine" => ""}, "two" => "=^.^="}},
:html => ""}
c = {:pets => {:dogs => {:my => {"niko" => ""}, "ollie" => ""}}, :cats =>
nil, :mice => ""}
d = {a: "", b: :two, c: ""}
Answers:
a.r_empty?, true
b.r_empty?, false
c.r_empty?, true
d.r_empty?, false
({}.r_empty?), true
Using this code, I was able to get the right answer for 4 out of the 5 tests. My method returns TRUE for b.r_empty? ... I do notice that if I uncomment out #puts value, "=^.^=" is printed out for b.r_empty? ... So the if statement is being executed, but ultimately false is not returned. I'm still a ruby novice so I will gladly appreciate any advice and guidance towards the right topics i should go over for this challenge.
Although it's cool to define a method inside another (I did not know this was possibly actually) the method can be simplified quite a bit:
class Hash
def r_empty?
!values.any? do |val|
val.is_a?(Hash) ? !val.r_empty? : (val && val != "")
end
end
end
I'm not sure exactly the problem is with your original code, however I think the recurse(value) is effectively being discarded.
By the way, in terms of style I recommend only using a ternary for single-line expressions and also being diligent about consistent indentation.

Check if a string contains only digits in ruby

I have a string which is passed as a parameter to a function. Here, I want to check if the string contains only numbers. So I had a check like below:
def check_string(string)
result = false
if string.to_i.to_s.eql? string
result = true
end
result
end
But the problem arises when a string starts with 0. In that case, a false is returned.
check_string('123') #=> true
check_string('0123') #=> false
How can I solve this issue?
You can try the following
def check_string(string)
string.scan(/\D/).empty?
end
It would be truthy if string contains only digits or if it is an empty string. Otherwise returns false.
A number can be negative, or a float. So if these are allowed, consider this solution:
def is_numberic?(str)
str == "#{str.to_f}" || str == "#{str.to_i}"
end
some input which evaluate to true
pry(main)> is_numberic? '5'
=> true
pry(main)> is_numberic? '58127721'
=> true
pry(main)> is_numberic? '58127721.737673'
=> true
pry(main)> is_numberic? '0'
=> true
pry(main)> is_numberic? '1818'
=> true
pry(main)> is_numberic? '0.1'
=> true
pry(main)> is_numberic? '0.0'
=> true
pry(main)> is_numberic? '11.29'
=> true
pry(main)> is_numberic? '-0.12'
=> true
pry(main)> is_numberic? '-29'
=> true
the input which evaluate to false
pry(main)> is_numberic? '10 years'
=> false
pry(main)> is_numberic? '01'
=> false
pry(main)> is_numberic? '00'
=> false
pry(main)> is_numberic? '0.10'
=> false
pry(main)> is_numberic? ''
=> false
As you can see, there're several cases which probably should be supported, eg '0.10', but are not. In this case, the permitted input is '0.1'.
def check_string(str)
str !~ /\D/
end
check_string '123'
#=> true
check_string ''
#=> true
check_string '1a2'
#=> false
this is my proposition for detecting if it's a float number
def check(string)
scan = string.scan(/\D/)
scan.size == 0 || (scan.size == 1 && scan.first == ".") # or "," depend on your separator
end
example of use:
check("123") => true
check("12.3") => true
check("12e3") => false
check("12.3.2") => false
EDIT: 2023
After some years i see this is the most compact solution:
def check_string(str)
str.scan(/\D/).empty?
end
You can use Regexp for it:
def check_string(string)
raise 'Empty string passed' if string.empty?
/\A\d+\z/ === string
end
check_string '123'
#=> true
check_string '0123'
#=> true
check_string '0'
#=> true
We can also use the "match" function to do this.
"1234".match(/\D/)
#=> nil
"1234foo".match(/\D/)
#=> #<MatchData "f">
match (String) - APIdock
I think we should use the regex to find this.
it will work for the below scenarios
"3.0"
"av3"
"3"
is_numeric = false if option.option.match?(/[^0-9.]/)
If anyone is searching for another way to determine if string is numeric -> is to use "is_a? Numeric". Is_a? reference documentation
"namaste".is_a? Numeric
=> false
6.is_a? Numeric
=> true
str1 = "foo"
str2 = 9
str1.is_a? Numeric
=> false
str2.is_a? Numeric
=> true
You can also use:
7.is_a?(Numeric)
=> true
"too".is_a?(Numeric)
=> false
Basically it's determining if a class is a type of class object. I just found this and thought I would share.

Hash#compare_by_identity with string literals

I'm running Ruby 2.2.1.
The following code runs as expected as string hash keys are duped and frozen:
f = 'foo'
h = {f => 'bar'}
h.compare_by_identity
h[f] # => nil
h['foo'] # => nil
h[h.keys.first] # => "bar"
But I can't for the life of me figure out what is going on here:
h = {'foo' => 'bar'}
h.compare_by_identity
h.keys.first.frozen? # => true
'foo'.frozen? # => false
h.keys.first.object_id # => 20421220
'foo'.object_id # => 20067280
h['foo'] # => "bar"
h['foo'.dup] # => nil
It's interesting to note the the docs for #compare_by_identity started using #dup at 2.2.0. So it seems this behavior change is known.
2.1.7:
h1["a"] #=> nil # different objects.
2.2.0:
h1["a".dup] #=> nil # different objects.
However, the source is the same.
The same does not happen with other literals like arrays. Any ideas on why this behavior changed for string literals? The docs give no hints as to why.

String includes another string or regex (Ruby)

I need to check if given String includes a string or regex. If it does, it should return true, otherwise - false. How can I do it?
I have:
def method(string)
if #text.match(/#{string}/)
true
else
false
end
end
But I'm not sure if it's a proper way.
Consider this:
#text = 'foobar'
def method1(string)
if #text.match(/#{string}/)
true
else
false
end
end
That can be reduced to:
def method2(string_or_regex)
if #text[string_or_regex]
true
else
false
end
end
String's [] method allows us to pass in a string or a pattern. If it's a string, the method uses it for a fixed-string/in-string search. If a pattern is passed in it returns the matching text.
However, Ruby is more friendly than this. We don't have to tell it to return a true or false value conditionally. In Ruby, a nil or false value is treated as false, and anything else is "truethy". We can convert a value to true/false using !!, which is double boolean "not":
true # => true
'foo' # => "foo"
false # => false
nil # => nil
!true # => false
!'foo' # => false
!false # => true
!nil # => true
!!true # => true
!!'foo' # => true
!!false # => false
!!nil # => false
Knowing that, and that String's [] returns a nil if not found, or the matching text if found:
'foo'['foo'] # => "foo"
'foo'['bar'] # => nil
we can reduce the original method to:
def method3(string_or_regex)
!!#text[string_or_regex]
end
Here's what happens testing each of the methods above:
method1('foo') # => true
method1('baz') # => false
method2('foo') # => true
method2(/foo/) # => true
method2('baz') # => false
method2(/baz/) # => false
method3('foo') # => true
method3(/foo/) # => true
method3('baz') # => false
method3(/baz/) # => false
You have to be careful interpolating a regular expression object into another regular expression:
string = /foo/
/#{string/ # => /(?-mix:foo)/
The (?-mix: are the pattern flags being inserted inside another pattern, which can open your pattern to all sorts of weird behaviors and create extremely hard to find bugs. Instead, I strongly recommend using the source method if you're going to do that, which results in the original pattern being inserted:
/#{string.source}/ # => /foo/
Code with single line:
!#text.match(/#{string}/).nil?

How can I check a word is already all uppercase?

I want to be able to check if a word is already all uppercase. And it might also include numbers.
Example:
GO234 => yes
Go234 => no
You can compare the string with the same string but in uppercase:
'go234' == 'go234'.upcase #=> false
'GO234' == 'GO234'.upcase #=> true
a = "Go234"
a.match(/\p{Lower}/) # => #<MatchData "o">
b = "GO234"
b.match(/\p{Lower}/) # => nil
c = "123"
c.match(/\p{Lower}/) # => nil
d = "µ"
d.match(/\p{Lower}/) # => #<MatchData "µ">
So when the match result is nil, it is in uppercase already, else something is in lowercase.
Thank you #mu is too short mentioned that we should use /\p{Lower}/ instead to match non-English lower case letters.
I am using the solution by #PeterWong and it works great as long as the string you're checking against doesn't contain any special characters (as pointed out in the comments).
However if you want to use it for strings like "Überall", just add this slight modification:
utf_pattern = Regexp.new("\\p{Lower}".force_encoding("UTF-8"))
a = "Go234"
a.match(utf_pattern) # => #<MatchData "o">
b = "GO234"
b.match(utf_pattern) # => nil
b = "ÜÖ234"
b.match(utf_pattern) # => nil
b = "Über234"
b.match(utf_pattern) # => #<MatchData "b">
Have fun!
You could either compare the string and string.upcase for equality (as shown by JCorc..)
irb(main):007:0> str = "Go234"
=> "Go234"
irb(main):008:0> str == str.upcase
=> false
OR
you could call arg.upcase! and check for nil. (But this will modify the original argument, so you may have to create a copy)
irb(main):001:0> "GO234".upcase!
=> nil
irb(main):002:0> "Go234".upcase!
=> "GO234"
Update: If you want this to work for unicode.. (multi-byte), then string#upcase won't work, you'd need the unicode-util gem mentioned in this SO question

Resources