How do I apply removing of characters to the string itself? - ruby

Using Ruby 2.4. How do I apply an editing of a stirng to the string itself? I have this method
# Removes the word from teh end of the string
def remove_word_from_end_of_str(str, word)
str[0...(-1 * word.length)]
end
I want the parameter to be operated upon, but it isn't working ...
2.4.0 :001 > str = "abc def"
=> "abc def"
2.4.0 :002 > StringHelper.remove_word_from_end_of_str(str, "def")
=> "abc "
2.4.0 :003 > str
=> "abc def"
I want the string that was passed in to be equal to "abc " but that isn't happening. I don't want to set the variable to the result of the function (e.g. "str = StringHelper.remove(...)"

Ruby already has the String#delete! method that does exactly this:
>> str = 'abc def'
=> "abc def"
>> word = 'def'
=> "def"
>> str.delete!(word)
=> "abc "
>> str
=> "abc "
Note that this will remove all instances of word:
>> str = 'def abc def'
=> "def abc def"
>> str.delete!(word)
=> " abc "
To limit the effect to only the last word, you can do:
>> str = 'def abc def'
=> "def abc def"
>> str.slice!(-word.length..-1)
=> "def"
>> str
=> "def abc "

str[range] is just a shorthand for str.slice(range). You just have to use the destructive method, like that :
# Removes the word from the end of the string
def remove_word_from_end_of_str(str, word)
str.slice!((str.length - word.length)...(str.length))
end
For more information, see the documentation.
If you want your function to return the new string as well, you should use :
# Removes the word from the end of the string
def remove_word_from_end_of_str(str, word)
str.slice!((str.length - word.length)...(str.length))
str
end

Try:
def remove_word_from_end_of_str(str, word)
str.slice!((str.length - word.length)..str.length)
end
Also, your explanation is a little confusing. You are calling the remove_word method as a class method but it is an instance method.

chomp! returns a the String with the given record separator removed from the end of string (if present), and nil if nothing was removed.
def remove_word_from_end_of_str(str, word)
str.chomp!( "CO")
end
str = "Aurora CO"
remove_word_from_end_of_str(str, "CO")
p str #=> "Aurora "

Related

Using `gsub` inside (double quoted) heredoc does not work

It appears that using gsub inside a (double quoted) heredoc does not evaluate the result of gsub, as follows:
class Test
def self.define_phone
class_eval <<-EOS
def _phone=(val)
puts val
puts val.gsub(/\D/,'')
end
EOS
end
end
Test.define_phone
test = Test.new
test._phone = '123-456-7890'
# >> 123-456-7890
# >> 123-456-7890
The second puts should have printed 1234567890, just as it would in this case:
'123-456-7890'.gsub(/\D/,'')
# => "1234567890"
What is going on inside the heredoc?
The problem is with the \D in the regex. It will be evaluated when the heredoc is evaluated as a string, which results in D:
"\D" # => "D"
eval("/\D/") #=> /D/
On the other hand, \D inside a single quote will not be evaluated as D:
'\D' # => "\\D"
eval('/\D/') # => /\D/
So wrap the heredoc terminator EOS in a single quote to achieve what you want:
class Test
def self.define_phone
class_eval <<-'EOS'
def _phone=(val)
puts val
puts val.gsub(/\D/,'')
end
EOS
end
end
Test.define_phone
test = Test.new
test._phone = '123-456-7890'
# >> 123-456-7890
# >> 1234567890
Reference
If you run the above code without the wrapped EOS, gsub will try to replace "D" (literally) in the val. See this:
test._phone = '123-D456-D7890DD'
# >> 123-D456-D7890DD
# >> 123-456-7890

Find index of regex match in string

Is it possible to find the index of a match in regex while still getting the match? For example:
str = "foo [bar] hello [world]"
str.match(/\[(.*?)\]/) { |match,idx|
puts match
puts idx
}
Unfortunately, idx is nil in this example.
My real world problem is a string, where I want to replace certain sub strings, that are wrapped in brackets with parentheses, based on some conditions (e.g. if the string is inside a blacklist), e.g. "foo [bar] hello [world]" should become "foo [bar] hello (world)" when the word world is in a blacklist.
You can use String#gsub:
blacklist = ["world"]
str = "foo [bar] hello [world]"
str.gsub(/\[(\w*?)\]/) { |m|
blacklist.include?($1) ? "(#{$1})" : m
}
#=> "foo [bar] hello (world)"
If you want an Enumerator with every match object, you can use :
def matches(string, regex)
position = 0
Enumerator.new do |yielder|
while match = regex.match(string, position)
yielder << match
position = match.end(0)
end
end
end
As an example :
p matches("foo [bar] hello [world]", /\[(.*?)\]/).to_a
# [#<MatchData "[bar]" 1:"bar">, #<MatchData "[world]" 1:"world">]
p matches("foo [bar] hello [world]", /\[(.*?)\]/).map{|m| [m[1], m.begin(0)]}
# [["bar", 4], ["world", 16]]
You can get the matched string and its index from the match object.
But actually, it looks like you need gsub with a block:
"foo [bar] hello [world]".gsub(/\[(.*?)\]/){ |m| # define logic here }

In Ruby - How to get gsub!(bang) not return nil if it doesn't match and make it behave like .gsub

Lets say i want to remove leading space from a string
a = " Hello world"
puts a.gsub!(/^ /,"") # => "Hello World"
But if there is no leading space in the string
b = "Hello World"
puts a.gsub!(/^ /,"") # => nil
Now if I use just gsub instead of gsub it returns the string:
puts b.gsub(/^ /,"") # => "Hello World"
puts a.gsub(/^ /,"") # => "Hello World" (works for both a and b)
So is it possible to get gsub! to perform like gsub as shown above?
the reason is because gsub would create a new object everytime which I would like to avoid because I will be using at least 4 or 5 gsubs on the string I need to manipulate.
thanks
Here are two ways of doing that.
Add || a to the gsub expression
a.gsub!(/\A\s/,"") || a
would give you the desired result, namely removing the space, if present, and returning the string after any change is made. If no change is made, this reduces to nil || a #=> a. If a change is made the expression reduces to a.gsub!(/\A\s/,""). For example,
a = " Hello"
a.gsub!(/\A\s/,"") || a #=> "Hello"
a #=> "Hello"
a ="Hello"
a.gsub!(/\A\s/,"") || a #=> "Hello"
a #=> "Hello"
Although it makes no difference here, I prefer to use the beginning of string anchor, \A, rather than the beginning of line anchor, ^, I also prefer using a whitespace character \s to a space (assuming it couldn't be a tab) to a space, as an inadvertent extra space might not be noticed.
Use String#replace and the non-destructive method String#gsub
a = " Hello"
a.replace(a.gsub(/\A\s/,"")) #=> "Hello"
a #=> "Hello"
a ="Hello"
a.replace(a.gsub(/\A\s/,"")) #=> "Hello"
a #=> "Hello"
Answer:
b.gsub!(/(^ )?/,"") => "Hello World"
Explanation:
(...) Capture everything enclosed
a? Zero or one of a
— via http://rubular.com
Example:
irb(main):008:0> a = " Hello World"
=> " Hello World"
irb(main):009:0> b = "Hello World"
=> "Hello World"
irb(main):010:0> a.gsub!(/(^ )?/,"")
=> "Hello World"
irb(main):011:0> b.gsub!(/(^ )?/,"")
=> "Hello World"
Alternative:
b.gsub!(/^ |/,"") # => "Hello World" using "OR" pipe char
The code below adds a method to the String class that simply calls gsub! and then returns the String object that it was called on. So if the gsub! was "successful" and something has been subbed it returns the subbed string. Otherwise it will return the original string.
class String
def filled_gsub!(oldsubstr, newsubstr)
gsub!(oldsubstr, newsubstr)
self
end
end

How to break the string into two lines in ruby syntax

How to break the string into two lines in ruby code?
Is there specific symbol?
def my_def
path = "//div/p[contains(., 'This is a veeeeeeeryyyyyy looooonggggg string')]"
end
I wish to make something like that:
def my_def
path = "//div/p[contains(., 'This is a veeeeeeeryyyyyy
looooonggggg string')]"
end
Back slashes doesn't work right!
Ruby will automatically concatenate two strings that are adjacent:
foo = 'a' 'b'
foo # => "ab"
Normally a line-end signifies the end of the assignment:
foo = 'a'
'b'
foo # => "a"
so you can't simply break the lines and expect Ruby to figure out what to do.
\ marks the line as continuing, so you could use:
foo = "a" \
"b"
foo # => "ab"
Or, rely on the + String concatenation:
foo = 'a' +
'b'
foo # => "ab"
I'd probably use the + since it's most often used to join strings already, so its meaning is very obvious. Using \ leads to people joining really long expressions instead of breaking them down.
If your strings are really long, you can use some other tricks:
foo = [
'foo',
'bar'
].join
foo # => "foobar"
If you want to join the strings with a space, such as recombining sentences:
foo = [
'foo',
'bar'
].join(' ')
foo # => "foo bar"
or:
foo = [
'foo',
'bar'
] * ' '
foo # => "foo bar"
Building on all that, I'd use some combination of the above or simply something like:
long_str = 'This is a veeeeeeeryyyyyy' +
' looooonggggg string'
path = "//div/p[contains(., '#{ long_str }')]"
or:
long_str = [
'This is a veeeeeeeryyyyyy',
'looooonggggg string'
].join(' ')
path = "//div/p[contains(., '%s')]" % long_str
You can use a backward slash to indicate the string continues on the next line, like so:
str = "this is a long \
string"
print str # => this is a long string
If your string gets way too big, it might be a good idea to use here docs instead. They allow you to write pieces of text in the middle of the code:
str = <<HEREDOC
This is my string :)
Let's imbue code in the imbued doc: #{[4, 2, 3, 1].sort}
HEREDOC
print str
# => This is my string :)
# => Let's imbue code in the imbued doc: [1, 2, 3, 4]
HEREDOC can be any name you want to give it. You can learn more about here docs here.

Reading a .txt file with escaped characters in Ruby

I'm having difficulty reading a file with escaped characters in Ruby...
My text file has the string "First Line\r\nSecond Line" and when I use File.read, I get a string back that escapes my escaped characters: "First Line\r\nSecond Line"
These two strings are not the same things...
1.9.2-p318 :006 > f = File.read("file.txt")
=> "First Line\\r\\nSecond Line"
1.9.2-p318 :007 > f.count('\\')
=> 2
1.9.2-p318 :008 > f = "First Line\r\nSecond Line"
=> "First Line\r\nSecond Line"
1.9.2-p318 :009 > f.count('\\')
=> 0
How can I get the File.read to not escape my escaped characters?
Create a method to remove all the additional escape characters that the File.Read method added, like this:
# Define a method to handle unescaping the escape characters
def unescape_escapes(s)
s = s.gsub("\\\\", "\\") #Backslash
s = s.gsub('\\"', '"') #Double quotes
s = s.gsub("\\'", "\'") #Single quotes
s = s.gsub("\\a", "\a") #Bell/alert
s = s.gsub("\\b", "\b") #Backspace
s = s.gsub("\\r", "\r") #Carriage Return
s = s.gsub("\\n", "\n") #New Line
s = s.gsub("\\s", "\s") #Space
s = s.gsub("\\t", "\t") #Tab
s
end
Then see it in action:
# Create your sample file
f = File.new("file.txt", "w")
f.write("First Line\\r\\nSecond Line")
f.close
# Use the method to solve your problem
f = File.read("file.txt")
puts "BEFORE:", f
puts f.count('\\')
f = unescape_escapes(f)
puts "AFTER:", f
puts f.count('\\')
# Here's a more elaborate use of it
f = File.new("file2.txt", "w")
f.write("He used \\\"Double Quotes\\\".")
f.write("\\nThen a Backslash: \\\\")
f.write('\\nFollowed by \\\'Single Quotes\\\'.')
f.write("\\nHere's a bell/alert: \\a")
f.write("\\nThis is a backspaces\\b.")
f.write("\\nNow we see a\\rcarriage return.")
f.write("\\nWe've seen many\\nnew lines already.")
f.write("\\nHow\\sabout\\ssome\\sspaces?")
f.write("\\nWe'll also see some more:\\n\\ttab\\n\\tcharacters")
f.close
# Read the file without the method
puts "", "BEFORE:"
puts File.read("file2.txt")
# Read the file with the method
puts "", "AFTER:"
puts unescape_escapes(File.read("file2.txt"))
You could just hack them back in.
foo = f.gsub("\r\n", "\\r\\n")
#=> "First Line\\r\\nSecond Line"
foo.count("\\")
#=> 2

Resources