regex ruby gsub: inserting space between characters in capture group - ruby

I am trying to to convert strings like:
"foo(bar baz)bom" => "foo (bar baz) bom"
I have this so far:
"foo(bar baz)bom".gsub(/((\S\()|(\)\S))/,'\1')
But I am not sure what to do with the '\1' to insert a space between the parens and the character.
Thanks!

"foo(bar baz)bom".gsub(/((?<! )\(|\)(?! ))/,"(" =>" (",")" =>") ")
will do the trick.

Do as below
>> "foo(bar baz)bom".gsub(/[)(]/,"(" =>" (",")" =>") ")
=> "foo (bar baz) bom"
>>
update
>> "foo (bar baz)bom".gsub(/(?<=\S)\(|\)(?=\S)/,"(" =>" (",")" =>") ")
=> "foo (bar baz) bom"
>> "foo(bar baz)bom".gsub(/(?<=\S)\(|\)(?=\S)/,"(" =>" (",")" =>") ")
=> "foo (bar baz) bom"
>> "foo(bar baz) bom".gsub(/(?<=\S)\(|\)(?=\S)/,"(" =>" (",")" =>") ")
=> "foo (bar baz) bom"
>>

This is easier:
"foo(bar baz)bom".gsub(/(?<!\s)(?=\()|(?<=\))(?!\s)/, " ")
# => "foo (bar baz) bom"

You were on the right track with \S\)|\)\S:
"foo(bar baz)bom".gsub /(?=\S\(|\)\S)(.)(.)/, '\1 \2'
#=> "foo (bar baz) bom"

If you don't absolutely have to use gsub, you can do it this way. It's more verbose, but to me it's easier to read than long complex regular expressions:
s = "foo(bar baz)bom"
while s =~ /\w\(/
s = $` + $&[0] + ' (' + $'
end
while s =~ /\)\w/
s = $` + ') ' + $&[1] + $'
end
Like I say, it's verbose. But it's straightforward and there's no guesswork.
Of course you can replace
$` + $&[0] + ' (' + $'
with
"#{$`}#{$&[0]} (#{$'}"
if you prefer to use string interpolation rather than + or <<. But I think the first form is easier to read.
If you aren't familiar with this notation, $` is the part of the string before the match, $& is the matched string, and $' is the part after the match. It's easy to remember which is which because it's left to right on your keyboard.

Related

Ruby - How to remove space after some characters?

I need to remove white spaces after some characters, not all of them. I want to remove whites spaces after these chars: I,R,P,O. How can I do it?
"I ".gsub(/(?<=[IRPO]) /, "") # => "I"
"A ".gsub(/(?<=[IRPO]) /, "") # => "A "
" P $ R 3I&".gsub(/([IRPO])\s+/,'\1')
#=> " P$ R3I&"

Evaluate a string with indexed array as values

I would like to take a string that contains positional argument markers (not named), supply it with an array (not hash) of values, and have it evaluated.
The use case as an example would be somewhat like ARGV.
For example,
# given:
string = "echo $1 ; echo $#"
values = ["hello", "world"]
# expected result:
"echo hello ; echo hello world"
The below function is the best I could come up with:
def evaluate_args(string, arguments)
return string unless arguments.is_a? Array and !arguments.empty?
# Create a variable that can replace $# with all arguments, and quote
# arguments that had "more than one word" originally
all_arguments = arguments.map{|a| a =~ /\s/ ? "\"#{a}\"" : a}.join ' '
# Replace all $1 - $9 with their respective argument ($1 ==> arguments[0])
string.gsub!(/\$(\d)/) { arguments[$1.to_i - 1] }
# Replace $# or $* with all arguments
string.gsub!(/\$[*|#]/, all_arguments)
return string
end
And it seems to me like it can and should be simpler.
I was hoping to find something that is closer to the Kernel.sprintf method of doing things - like "string with %{marker}" % {marker: 'value'}
So, although this issue is almost solved for me (I think), I would love to know if there is something I missed that can make it more elegant.
It seems like you're trying to reproduce Bash-style variable expansion, which is an extremely complex problem. At the very least, though, you can simplify your code in two ways:
Use Kernel.sprintf's built in positional argument feature. The below code does this by substituting e.g. $1 with the sprintf equivalent %1$s.
Use Shellwords from the standard library to escape arguments with spaces etc.
require 'shellwords'
def evaluate_args(string, arguments)
return string unless arguments.is_a? Array and !arguments.empty?
tmpl = string.gsub(/\$(\d+)/, '%\1$s')
(tmpl % arguments).gsub(/\$[*#]/, arguments.shelljoin)
end
string = "echo $1 ; echo $#"
values = ["hello", "world"]
puts evaluate_args(string, values)
# => echo hello ; echo hello world
If you didn't have the $* requirement I'd suggest just dropping the Bash-like format and just using sprintf, since it covers everything else you mentioned. Even so, you could further simplify things by using sprintf formatting for everything else:
def evaluate_args(string, arguments)
return string unless arguments.is_a? Array and !arguments.empty?
string.gsub('%#', arguments.shelljoin) % arguments
end
string = "echo %1$s ; echo %#"
values = ["hello", "world"]
puts evaluate_args(string, values)
# => echo hello ; echo hello world
Edit
If you want to use %{1} with sprintf you could turn the input array into a hash where the integer indexes are turned into symbol keys, e.g. ["hello", "world"] becomes { :"1" => "hello", :"2" => "world" }:
require "shellwords"
def evaluate_args(string, arguments)
return string unless arguments.is_a? Array and !arguments.empty?
string % {
:* => arguments.shelljoin,
**arguments.map.with_index {|val,idx| [ :"#{idx + 1}", val ] }.to_h
}
end
string = "echo %{1} ; echo %{*}"
values = ["hello", "world"]
puts evaluate_args(string, values)
# => echo hello ; echo hello world
string = "echo $1 ; echo $# ; echo $2 ; echo $cat"
values = ["hello", "World War II"]
vals = values.map { |s| s.include?(' ') ? "\"#{s}\"" : s }
#=> ["hello", "\"World War II\""]
all_vals = vals.join(' ')
#=> "hello \"World War II\""
string.gsub(/\$\d+|\$[#*]/) { |s| s[/\$\d/] ? vals[s[1..-1].to_i-1] : all_vals }
#=> "echo hello ; echo hello \"World War II\" ; echo \"World War II\" ; echo $cat" $cat"

Replace string characters with a single comma

I want to remove unwanted symbols from this string:
myString = String.new("##106#107#Hello##106#World##")
and separate each word with a comma. The desired result is:
"Hello, World"
Any ideas on how to achieve my desired result? What I tried is this:
myString.gsub!(/[##0123456789]/, ',')
print myString + "\n"
# => ",,,,,,,,,,Hello,,,,,,World,,"
There are a couple of ways:
myString.scan(/[a-zA-Z]+/).join(', ')
=> "Hello, World"
myString.gsub(/(\A)?[^a-zA-Z]+(\z)?/){$1 || $2 ? "" : ", "}
# => "Hello, World"

Succinct way in Ruby to manipulate this string

Sometimes I like learning how to do things the "Ruby" way. I was wondering - what is the most succinct, yet readable way to take a string such as:
foo-bar
and manipulate it to read:
Foo Bar
"foo-bar".split("-").map(&:capitalize).join(" ")
"foo-bar".gsub(/\b(\w)/){|m| m.capitalize}.sub '-', ' '
>> p "foo-bar".scan(/\w+/).map(&:capitalize).join(" ")
"Foo Bar"
=> "Foo Bar"
>> p "foo---bar".scan(/\w+/).map(&:capitalize).join(" ")
"Foo Bar"
=> "Foo Bar"
>> p "foo 123 bar".scan(/\w+/).map(&:capitalize).join(" ")
"Foo 123 Bar"
=> "Foo 123 Bar"
string = "foo-bar"
"foo-bar".split("-").map(&:capitalize).join(" ") # edited to because former answer was not optimal

Building multi-line strings, programmatically, in Ruby

Here's something I often do when programming:
code = ''
code << "next line of code #{something}" << "\n"
code << "another line #{some_included_expression}" << "\n"
Is there some better way than having << "\n" or + "\n" on every line? This seems quite inefficient.
I'm interested in Ruby solutions, in particular. I'm thinking something like
code = string.multiline do
"next line of code #{something}"
"another line #{some_included_expression}"
end
If you're looking to build a block of text, the easy way to do it is to just use the % operator. For example:
code = %{First line
second line
Third line #{2 + 2}}
'code' will then be
"First line\n second line\n Third line 4"
This would be one way:
code = []
code << "next line of code #{something}"
code << "another line #{some_included_expression}"
code.join("\n")
Use <<- operator:
code = <<-CODE
var1 = "foo"
var2 = "bar"
CODE
It would work for you to just embed ...\n" in your strings, I suppose. Here is a fun way to do it:
class String
def / s
self << s << "\n"
end
end
then
f = "" # => ""
f / 'line one' # => "line one\n"
f / 'line two' # => "line one\nline two\n"
f / 'line three' # => "line one\nline two\nline three\n"
This would enable something like:
"" / "line 1" / "line 2" / "line 3" # => "line 1\nline 2\nline 3\n"
Or even:
f/
"line one"/
"line two"/
"line three" # => "line one\nline two\nline three\n"
Here's a method presented here:
str = <<end.margin
|This here-document has a "left margin"
|at the vertical bar on each line.
|
| We can do inset quotations,
| hanging indentions, and so on.
end
This is accomplished by using this:
class String
def margin
arr = self.split("\n") # Split into lines
arr.map! {|x| x.sub!(/\s*\|/,"")} # Remove leading characters
str = arr.join("\n") # Rejoin into a single line
self.replace(str) # Replace contents of string
end
end
I guess the question with this is: does the lack of portability / presence of monkey patching make this solution bad.
What's wrong with:
code = "next line of code #{something}\n"+
"another line #{some_included_expression}"
You could place your multi-line text in a file, and use ERB to parse it (note ERB is included with Ruby)
require 'erb'
multi_line_string = File.open("multi_line_string.erb", 'r').read
template = ERB.new(multi_line_string)
template.result(binding)
(ERB can access variables from a Binding, an object that provides access to the instance methods and variables that are owned by another object. By setting it to "binding" it points to itself)
Documentation here.

Resources