String format works like this:
someString = "Example string %{key}"
result = someString % {key: newData}
I would like to retrieve the hash keys in the string without hardcoding them. Is there a method for doing this?
Also, is there any way to construct the format string using variables? I would like to do something like this:
variable = key
result = someString % {variable: newData}
You almost got it. Just a bit off with the syntax
variable = :key # get this one from whereever
someString = "Example string %{key}"
someString % { variable => 'foo' } # => "Example string foo"
One way to extract keys from the format string:
"Example string %{key1} %{key2}".scan /(?<=%{)[^{}]+?(?=})/
# => ["key1", "key2"]
The regex (?<=%{)[^{}]+?(?=}) matches one or more characters (non-greedy) if it's prefixed by %{ and followed by }.
To construct the format string, you can use string interpolation:
variable = 'key'
"Example string %{#{variable}}"
# => "Example string %{key}"
Related
I am attempting to replace a variable in a large search string I will be using to call Elastic Search, but I can't seem to get the variable into the string. Below is what I have attempted, using string interpolation to try and add my_var into the search string at 3 different places.
my_var = "foo"
search_string = '{"query":{"bool":{"must":[{"nested":{"path":"some_status", "query":{"bool":{"must":{"terms":{"status.code":["ACTIVE"]}}}}}}, {"bool":{"should":[{"nested":{"path":"acc", "query":{"bool":{"must":[{"match_phrase":{"acc.name.text":"{#my_var}"}}]}}}}, {"bool":{"must":[{"match_phrase":{"name.stuff":"{#my_var}"}}]}}, {"nested":{"path":"trade_names", "query":{"bool":{"must":[{"match_phrase":{"some_names.some_name.stuff":"{#my_var}"}}]}}}}]}}]}}}'
JSON.parse(search_string)
What am I doing wrong here?
First of all, it's #{my_var}, not {#my_var}.
Beside that typo, string interpolation needs "...", it is disabled in '...':
foo = 123
str = "foo = #{foo}" # <- turns #{foo} into 123
#=> "foo = 123"
str = 'foo = #{foo}' # <- keeps #{foo} literally
#=> "foo = \#{foo}"
Since your string contains many literal double quote characters, using "..." would add a lot of escape characters.
Apart from quotes, you could use the percent string %Q(...):
str = %Q({"foo":"#{foo}"})
#=> "{\"foo\":\"123\"}"
or a heredoc: (using JSON as the delimiter might enable syntax highlighting in your editor)
str = <<-JSON.chomp
{"foo":"#{foo}"}
JSON
#=> "{\"foo\":\"123\"}"
Another option is to construct a Ruby hash and turn that into JSON:
require 'json'
hash = { 'foo' => foo }
str = hash.to_json
#=> "{\"foo\":123}"
The JSON library also handles escaping for you.
I have a String like this:
price<=>656000<br>bathrooms<=>1<br>bedrooms<=>3<br>pets<=>1<br>surface<=>60<br>brokerfree<=>1
model<=>opel/corsa<br>mileage<=>67000<br>vinnumber<=>unknown<br>price<=>145000<br>year<=>2010<br>condition<=>2<br>transmission<=>unknown<br>cartype<=>1
I want a Hash:
:model => 'opel/corsa'
etc etc... the string is variable so this is also valid:
year<=>2015<br>condition<=>1<br>price<=>2100mileage<=>22000<br>price<=>120000<br>year<=>2012<br>condition<=>2
or this
price<=>656000<br>bathrooms<=>1<br>bedrooms<=>3<br>pets<=>1<br>surface<=>60<br>brokerfree<=>1
model<=>opel/corsa<br>mileage<=>67000<br>vinnumber<=>unknown<br>price<=>145000<br>year<=>2010<br>condition<=>2<br>transmission<=>unknown<br>cartype<=>1
You don't need a regex. You can use plain ruby methods.
array = string.split('<br>')
hash = Hash[array.map {|el| el.split('<=>') }]
You can also use .to_h method of Array, see the following example.
string = "model<=>opel/corsa<br>mileage<=>67000<br>vinnumber<=>unknown<br>price<=>145000<br>year<=>2010<br>condition<=>2<br>transmission<=>unknown<br>cartype<=>1"
hash = string.split('<br>').map{|a| a.split('<=>')}.to_h
## OUTPUT
{"model"=>"opel/corsa", "mileage"=>"67000", "vinnumber"=>"unknown", "price"=>"145000", "year"=>"2010", "condition"=>"2", "transmission"=>"unknown", "cartype"=>"1"}
If str is your string, you can construct your hash as follows:
Hash[*str.split(/<=>|<br>/)]
#=> {"price"=>"145000", "bathrooms"=>"1", "bedrooms"=>"3", "pets"=>"1",
# "surface"=>"60", "brokerfree"=>"1", "model"=>"opel/corsa",
# "mileage"=>"67000", "vinnumber"=>"unknown", "year"=>"2010",
# "condition"=>"2", "transmission"=>"unknown", "cartype"=>"1"}
A second example:
str = "year<=>2015<br>condition<=>1<br>price<=>2100<br>mileage<=>22000"+
"<br>price<=>120000<br>year<=>2012<br>condition<=>2"
Hash[*str.split(/<=>|<br>/)]
#=> {"year"=>"2012", "condition"=>"2", "price"=>"120000", "mileage"=>"22000"}
I have loaded a string with #{variable} references in it. How would I resolve those variables in the string like puts does?
name="jim"
str="Hi #{name}"
puts str
Instead of puts, I would like to have the result available to pass as a parameter or save into a variable.
you could eval it
name = "Patrick"
s = 'hello, #{name}'
s # => "hello, \#{name}"
# wrap the string in double quotes, making it a valid interpolatable ruby string
eval "\"#{s}\"" # => "hello, Patrick"
puts doesn't resolve the variables. The Ruby parser does when it creates the string. if you passed str to any other method, it would be the same as passing 'Hi jim', since the interpolation is already done.
String has a format option that appears as %. It can be used to pass arguments into a predefined string much like interpolation does.
message = "Hello, %s"
for_patrick = message % "Patrick" #=> "Hello, Patrick"
for_jessie = message % "Jessie" #=> "Hello, Jessie"
messages = "Hello, %s and %s"
for_p_and_j = messages % ["Patrick", "Jessie"] #=> "Hello, Patrick and Jessie"
It may not look "Rubyish" but I believe it is the functionality you are looking for.
So, if you have a string coming in from somewhere that contains these placeholders, you can then pass in values as arguments as so:
method_that_gets_hello_message % "Patrick"
This will also allow you to only accept values you are expecting.
message = "I can count to %d"
message % "eleven" #=> ArgumentError: invalid value for Integer()
There's a list on Wikipedia for possible placeholders for printf() that should also work in Ruby.
The eval seems to be the only solution for this particular task. But we can avoid this dirty-unsafe-dishonourable eval if we modify the task a bit: we can resolve not local, but instance variable without eval using instance_variable_get:
#name = "Patrick"
#id = 2 # Test that number is ok
#a_b = "oooo" # Test that our regex can eat underscores
s = 'hello, #{name} !!#{id} ??#{a_b}'
s.gsub(/#\{(\w+)\}/) { instance_variable_get '#'+$1 }
=> "hello, Patrick !!2 ??oooo"
In this case you even can use any other characters instead of #{} (for example, %name% etc), by only modifying the regex a bit.
But of course, all this smells.
It sounds like you want the basis for a template system, which Ruby does easily if you use String's gsub or sub methods.
replacements = { '%greeting%' => 'Hello', '%name%' => 'Jim' }
pattern = Regexp.union(replacements.keys)
'%greeting% %name%!'.gsub(pattern, replacements)
=> "Hello Jim!"
You could just as easily define the key as:
replacements = { '#{name}' => 'Jim' }
and use Ruby's normal string interpolation #{...} but I'd recommend not reusing that. Instead use something unique.
The advantage to this is the target => replacement map can easily be put into a YAML file, or a database table, and then you can swap them out with other languages, or different user information. The sky is the limit.
The benefit to this also, is there is no evaluation involved, it's only string substitution. With a bit of creative use you can actually implement macros:
macros = { '%salutation%' => '%greeting% %name%' }
replacements = { '%greeting%' => 'Hello', '%name%' => 'Jim' }
macro_pattern, replacement_pattern = [macros, replacements].map{ |h| Regexp.union(h.keys) }
'%salutation%!'.gsub(macro_pattern, macros).gsub(replacement_pattern, replacements)
=> "Hello Jim!"
I have a string that looks something like this:
"my name is: andrew"
I'd like to parse the string, pull out the name from the string, and assign it to a variable. How would I do this with Ruby?
Update:
The string I used as an example was only an example. The strings that I will be working with can change formats, so you can't rely on the colon being in the actual example. Here are a few examples that I'm working with:
"/nick andrew" # command: nick, value: "andrew"
"/join developers" # command: join, value: "developers"
"/leave" # command: leave, value: nil
I'd like to use some sort of regular expression to solve this (since the string can change formats), rather than splitting the string on certain characters or relying on a certain character position number.
s = "my name is: andrew"
p s.split(':')[1].strip # "andrew"
See
split
strip
Another way:
name = "my name is: andrew".split(/: */)[1] # => "andrew"
or
name = "my name is: andrew".split(/: */).last # => "andrew"
Breaking it down, first we break it into parts.
The regular expression /: */ says a : followed by any number of spaces will be our splitter.
"my name is: andrew".split(/: */) # => ["my name is", "andrew"]
Then we select the second item:
["my name is", "andrew"][1] # => "andrew"
This tutorial really helped me understand how to work with regular expressions in Ruby.
One way to use a regular expression to get the string you want is to replace the stuff you don't want with an empty string.
original_string = "my name is: andrew"
name = original_string.sub(/^my name is: /, '') # => 'andrew'
another_format = "/nick andrew"
name = another_format.sub(/^\/nick /, '') # => 'andrew'
However, that's just string replacement/substitution. The regex is not capturing anyting.
To capture a string using a regular expression, you can use the Ruby match method:
original_string = "my name is: andrew"
matches = original_string.match /^my name is: (.*)/
name = matches[1] # return the first match
One way to do that is:
s = "my name is: andrew"
pos = (s =~ /(?!.*:).*/)
result = s[pos..-1]
p result.strip! # "andrew"
Another:
s = "my name is: andrew";
p s.slice(s.index(":")..-1) # "andrew"
s.split.last
That should work with all of your cases.
Instead of saying:
#b = coll.find("xpto.it" => #email)
if it's equal to #email, how can I look if it contains the string #email?
EDIT
It doesn't work when there's something like:
"Donald Trump <donal#tr.com>"
You can also construct a regular expression dynamically. To match a given search string:
search_string = #email
# Constructor syntax
coll.find({"name" => Regexp.new(search_string)})
# Literal syntax
coll.find({"name" => /#{search_string}/})
Reference:
http://api.mongodb.org/ruby/current/file.TUTORIAL.html