Python has the following elegant syntax for checking whether one string is a substring of another one:
'ab' in 'abc' # True
Is there an equivalent elegant syntax in Ruby?
I'm aware to the "abc".includes? "ab" Ruby syntax, but I'm wondering whether the inverse syntax exists too (where the first parameter is the substring and the second is the string).
There isn't such method in Ruby standard library, but Rails ActiveSupport provides #.in? method:
1.9.3-p484 :004 > "ab".in? "abc"
=> true
Here is the source code: https://github.com/rails/rails/blob/e20dd73df42d63b206d221e2258cc6dc7b1e6068/activesupport/lib/active_support/core_ext/object/inclusion.rb
Define "elegant".
This does a sub-string search and returns the "hit" if found:
'abc'['ab'] # => "ab"
Using !! converts the value returned to a true/false, so "ab" becomes true:
!!'abc'['ab'] # => true
Knowing that, it's trivial to add it in if you want something closer:
class String
def in?(other)
!!other[self]
end
end
'ab'.in?('abc') # => true
'ab'.in? 'abc' # => true
Or, use require 'active_support/core_ext/object/inclusion' to cherry-pick the Active Suport definition that extends all objects to allow in?. See http://edgeguides.rubyonrails.org/active_support_core_extensions.html#in-questionmark. The upside/downside to that it's modifying all objects.
Related
I am using Ruby 1.9.
I have a hash:
Hash_List={"ruby"=>"fun to learn","the rails"=>"It is a framework"}
I have a string like this:
test_string="I am learning the ruby by myself and also the rails."
I need to check if test_string contains words that match the keys of Hash_List. And if it does, replace the words with the matching hash value.
I used this code to check, but it is returning them empty:
another_hash=Hash_List.select{|key,value| key.include? test_string}
OK, hold onto your hat:
HASH_LIST = {
"ruby" => "fun to learn",
"the rails" => "It is a framework"
}
test_string = "I am learning the ruby by myself and also the rails."
keys_regex = /\b (?:#{Regexp.union(HASH_LIST.keys).source}) \b/x # => /\b (?:ruby|the\ rails) \b/x
test_string.gsub(keys_regex, HASH_LIST) # => "I am learning the fun to learn by myself and also It is a framework."
Ruby's got some great tricks up its sleeve, one of which is how we can throw a regular expression and a hash at gsub, and it'll search for every match of the regular expression, look up the matching "hits" as keys in the hash, and substitute the values back into the string:
gsub(pattern, hash) → new_str
...If the second argument is a Hash, and the matched text is one of its keys, the corresponding value is the replacement string....
Regexp.union(HASH_LIST.keys) # => /ruby|the\ rails/
Regexp.union(HASH_LIST.keys).source # => "ruby|the\\ rails"
Note that the first returns a regular expression and the second returns a string. This is important when we embed them into another regular expression:
/#{Regexp.union(HASH_LIST.keys)}/ # => /(?-mix:ruby|the\ rails)/
/#{Regexp.union(HASH_LIST.keys).source}/ # => /ruby|the\ rails/
The first can quietly destroy what you think is a simple search, because of the ?-mix: flags, which ends up embedding different flags inside the pattern.
The Regexp documentation covers all this well.
This capability is the core to making an extremely high-speed templating routine in Ruby.
You could do that as follows:
Hash_List.each_with_object(test_string.dup) { |(k,v),s| s.sub!(/#{k}/, v) }
#=> "I am learning the fun to learn by myself and also It is a framework."
First, follow naming conventions. Variables are snake_case, and names of classes are CamelCase.
hash = {"ruby" => "fun to learn", "rails" => "It is a framework"}
words = test_string.split(' ') # => ["I", "am", "learning", ...]
another_hash = hash.select{|key,value| words.include?(key)}
Answering your question: split your test string in words with #split and then check whether words include a key.
For checking if the string is substring of another string use String#[String] method:
another_hash = hash.select{|key, value| test_string[key]}
Can I check if a local variable is defined given it's name as string?
I know there is the function defined?, but you have to give the variable itself.
Example:
a = 'cat'
print defined?(a) # => "cat"
print defined?(b) # => nil
What I need is:
a = 'cat'
print string_defined?("a") # => "cat"
print string_defined?("b") # => nil
Or something like that. I can't find it in the docs...
I tried to use respond_to?, but doesn't seems to work...
Starting with Ruby 2.1.0 you can use Binding#local_variable_defined?:
a = 'cat'
binding.local_variable_defined? 'a' #=> true
binding.local_variable_defined? 'b' #=> false
The following will return true when the local variable in question is (to be) defined in the context, not necessary in a position preceding the point of it:
local_variables.include?("a".to_sym)
#=> true
You can do it using eval:
a = 'cat'
eval("defined?(#{'a'})")
=> "local-variable"
eval("defined?(#{'b'})")
=> nil
Disclaimer: This answer makes use of eval, so it can be dangerous if you don't strictly control the string you want to pass into it. And definitely you shouldn't do it this way if these strings come from user input.
I am new to YAML and Ruby. I am using the following Ruby code to parse a YAML file:
obj = YAML::load_file('test.yml')
Are the following YAML file contents for 'test.yml' valid?
Case 1:
test
In this case, I don't specify the value of test (something like test : true) but my Ruby parsing code does not throw an error. I thought this was an invalid YAML syntax.
Case 2:
:test : true
In this case, the Ruby code treats test as a symbol instead of a string and when I do puts obj[:test], it returns the result to be "true". Is this a Ruby thing? Other languages will interpret it as a string ":test"?
Case 3:
:test : true
:test : false
In this case, instead of throwing up an error for redefinition of :test, my Ruby code takes the latest value for :test (which is false). Why is this? Does YAML syntax allow for re-definition of elements and in which case only the latest value gets taken?
Case 1: YAML allows unquoted scalars, or "bare" strings not enclosed in quotes. Compared to quoted strings they are less flexible, since you can't use certain characters without creating ambiguous syntax, but the Ruby parser does support them.
1.9.3-p448 > YAML::parse('test').to_ruby
=> "test"
Case 2: As you've guessed, this is Ruby-specific since YAML has no concept of "symbols". When converting a YAML mapping to a Ruby hash, scalar keys that start with a colon are interpreted as symbols instead of strings.
Case 3: Under YAML's definition of a mapping, keys must be unique, so a strict parser should throw an error when given your example. It seems the Ruby parser is more lenient, and allows the same key to be defined multiple times with a last-value-wins rule. This is also allowed in native Ruby hashes.
1.9.3-p448 > YAML::parse("test: true\ntest: false").to_ruby
=> {"test"=>false}
1.9.3-p448 > { 'test' => true, 'test' => false }
=> {"test"=>false}
A great way to learn how the YAML parser converts to/from Ruby structures, is to write Ruby code that outputs YAML, and look at what it's doing:
Here's a basic hash:
require 'yaml'
foo = {'test' => true} # => {"test"=>true}
foo.to_yaml # => "---\ntest: true\n"
A hash using a symbol as a key:
foo = {test: true}
foo.to_yaml # => "---\n:test: true\n"
A hash with conflicting keys, causing the first to be stomped-on by the last:
foo = {test: true, test: false}
foo # => {:test=>false}
foo.to_yaml # => "---\n:test: false\n"
YAML is creating the hash, but hashes can't have duplicated keys; If they do, the collision results in the second replacing the first.
"Yaml Cookbook
at the YamlForRuby site" is also a great resource.
I have just started playing with Ruby and I'm stuck on something. Is
there some trick to modify the casefold attribute of a Regexp object after
it's been instantiated?
The best idea what I tried is the following:
irb(main):001:0> a = Regexp.new('a')
=> /a/
irb(main):002:0> aA = Regexp.new(a.to_s, Regexp::IGNORECASE)
=> /(?-mix:a)/i
But none of the below seems to work:
irb(main):003:0> a =~ 'a'
=> 0
irb(main):004:0> a =~ 'A'
=> nil
irb(main):005:0> aA =~ 'a'
=> 0
irb(main):006:0> aA =~ 'A'
=> nil
Something I don't understand is happening here. Where did the 'i' go on line
8?
irb(main):07:0> aA = Regexp.new(a.to_s, Regexp::IGNORECASE)
=> /(?-mix:a)/i
irb(main):08:0> aA.to_s
=> "(?-mix:a)"
irb(main):09:0>
I am using Ruby 1.9.3.
I am also unable understand the below code: why returning false:
/(?i:a)/.casefold? #=> false
As your console output shows, a.to_s includes the case sensitiveness as an option for your subexpression, so aA is being defined as
/(?-mix:a)/i
so you're asking ruby for a regular expression that is case insensitive, but the only thing in that case insensitive regexp is a group for when case sensitivity has be turned on, so the net effect is that 'a' is matched case sensitively
Since the result of to_s is just the regular expression string itself - no delimiters or external flags - the flags are translated into the (?i:...) syntax that sets or clears them temporarily inside the expression itself. This lets you get a Regexp object back out via a simple Regexp.new(s) call that will match the same strings.
The wrapping, unfortunately, includes explicitly clearing the flags that are not set on the object. So your first regex gets stringified into something between (?:-i...) - that is, the casefold option is explicitly turned off between the parentheses. Turning it back on for the object doesn't have any effect.
You can use a.source instead of a.to_s to get just the original expression, without the flag settings:
irb(main):001:0> a=/a/
=> /a/
irb(main):002:0> aA = Regexp.new(a.source, Regexp::IGNORECASE)
=> /a/i
irb(main):003:0> a =~ 'a'
=> 0
irb(main):004:0> a =~ 'A'
=> nil
irb(main):005:0> aA =~ 'a'
=> 0
irb(main):006:0> aA =~ 'A'
=> 0
As Frederick already explains, calling to_s on a regex will add modifiers around it that ensure that its properties like case-sensitiveness are preserved. So if you insert a case-sensitive regex into a case-insensitive regex, the inserted part will still be case-sensitive. Likewise the modifiers given to Regexp.new will have no effect if the first argument is a regex or the result of calling to_s on one.
To solve this issue, call source on the regex instead of to_s. Unlike to_s, source simply returns the source of regex without adding anything:
aA = Regexp.new(a.source, Regexp::IGNORECASE)
I am also unable understand the below code: why returning false:
/(?i:a)/.casefold?
Because (?i:...) sets the i flag locally, not globally. It only applies to the part of the regex within the parentheses, not the whole regex. Of course in this case the whole regex is within the parentheses, but that doesn't matter as far as methods like casefold? are concerned.
Does Ruby have a some_string.starts_with("abc") method that's built in?
It's called String#start_with?, not String#startswith: In Ruby, the names of boolean-ish methods end with ? and the words in method names are separated with an _. On Rails you can use the alias String#starts_with? (note the plural - and note that this method is deprecated). Personally, I'd prefer String#starts_with? over the actual String#start_with?
Your question title and your question body are different. Ruby does not have a starts_with? method. Rails, which is a Ruby framework, however, does, as sepp2k states. See his comment on his answer for the link to the documentation for it.
You could always use a regular expression though:
if SomeString.match(/^abc/)
# SomeString starts with abc
^ means "start of string" in regular expressions
If this is for a non-Rails project, I'd use String#index:
"foobar".index("foo") == 0 # => true
You can use String =~ Regex. It returns position of full regex match in string.
irb> ("abc" =~ %r"abc") == 0
=> true
irb> ("aabc" =~ %r"abc") == 0
=> false