Succinct way in Ruby to manipulate this string - ruby

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

Related

Display Unique Shell Columns

Given we have two formatted strings that are unrelated to each other.
#test.rb
string_1 = "Title\nfoo bar\nbaz\nfoo bar baz boo"
string_2 = "Unrelated Title\ndog cat farm\nspace moon"
How can I use ruby or call shell commands to have each of these string display as columns in terminal? The key is that the data of each string are not building a correlated row, ie this is not a table, rather 2 lists side by side.
Title Unrelated Title
foo bar dog cat farm
baz space moon
foo bar baz boo
You can try using paste and column command together. Note that this is a shell command so spaces between the assignment operator should be corrected.
$ string_1="Title\nfoo bar\nbaz\nfoo bar baz boo"
$ string_2="Unrelated Title\ndog cat farm\nspace moon"
$ paste -d '|' <(echo -e "$string_1") <(echo -e "$string_2") | column -s'|' -t
Title Unrelated Title
foo bar dog cat farm
baz space moon
foo bar baz boo
We paste the lines with | as delimiter and tell column command to use | as a reference to form columns.
In Ruby, you could do it this way:
#!/usr/bin/env ruby
string_1 = "Title\nfoo bar\nbaz\nfoo bar baz boo"
string_2 = "Unrelated Title\ndog cat farm\nspace moon"
a1 = string_1.split("\n")
a2 = string_2.split("\n")
a1.zip(a2).each { |pair| puts "%-20s%s" % [pair.first, pair.last] }
# or
# a1.zip(a2).each { |left, right| puts "%-20s%s" % [left, right] }
This produces:
Title Unrelated Title
foo bar dog cat farm
baz space moon
foo bar baz boo
Hi , If you Use temp files
string_1 = "Title\nfoo bar\nbaz\nfoo bar baz boo"
string_2 = "Unrelated Title\ndog cat farm\nspace moon"
echo -e $string_1 >a.txt
echo -e $string_2 >b.txt
paste a.txt b.txt
I hope it will help.

regex ruby gsub: inserting space between characters in capture group

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.

Ruby scan Regular Expression

I'm trying to split the string:
"[test| blah] \n [foo |bar bar bar]\n[test| abc |123 | 456 789]"
into the following array:
[
["test","blah"]
["foo","bar bar bar"]
["test","abc","123","456 789"]
]
I tried the following, but it isn't quite right:
"[test| blah] \n [foo |bar bar bar]\n[test| abc |123 | 456 789]"
.scan(/\[(.*?)\s*\|\s*(.*?)\]/)
# =>
# [
# ["test", "blah"]
# ["foo", "bar bar bar"]
# ["test", "abc |123 | 456 789"]
# ]
I need to split at every pipe instead of the first pipe. What would be the correct regular expression to achieve this?
s = "[test| blah] \n [foo |bar bar bar]\n[test| abc |123 | 456 789]"
arr = s.scan(/\[(.*?)\]/).map {|m| m[0].split(/ *\| */)}
Two alternatives:
s = "[test| blah] \n [foo |bar bar bar]\n[test| abc |123 | 456 789]"
s.split(/\s*\n\s*/).map{ |p| p.scan(/[^|\[\]]+/).map(&:strip) }
#=> [["test", "blah"], ["foo", "bar bar bar"], ["test", "abc", "123", "456 789"]]
irb> s.split(/\s*\n\s*/).map do |line|
line.sub(/^\s*\[\s*/,'').sub(/\s*\]\s*$/,'').split(/\s*\|\s*/)
end
#=> [["test", "blah"], ["foo", "bar bar bar"], ["test", "abc", "123", "456 789"]]
Both of them start by splitting on newlines (throwing away surrounding whitespace).
The first one then splits each chunk by looking for anything that is not a [, |, or ] and then throws away extra whitespace (calling strip on each).
The second one then throws away leading [ and trailing ] (with whitespace) and then splits on | (with whitespace).
You cannot get the final result you want with a single scan. About the closest you can get is this:
s.scan /\[(?:([^|\]]+)\|)*([^|\]]+)\]/
#=> [["test", " blah"], ["foo ", "bar bar bar"], ["123 ", " 456 789"]]
…which drops information, or this:
s.scan /\[((?:[^|\]]+\|)*[^|\]]+)\]/
#=> [["test| blah"], ["foo |bar bar bar"], ["test| abc |123 | 456 789"]]
…which captures the contents of each "array" as a single capture, or this:
s.scan /\[(?:([^|\]]+)\|)?(?:([^|\]]+)\|)?(?:([^|\]]+)\|)?([^|\]]+)\]/
#=> [["test", nil, nil, " blah"], ["foo ", nil, nil, "bar bar bar"], ["test", " abc ", "123 ", " 456 789"]]
…which is hardcoded to a maximum of four items, and inserts nil entries that you would need to .compact away.
There is no way to use Ruby's scan to take a regex like /(?:(aaa)b)+/ and get multiple captures for each time the repetition is matched.
Why the hard path (single regex)? Why not a simple combo of splits? Here are the steps, to visualize the process.
str = "[test| blah] \n [foo |bar bar bar]\n[test| abc |123 | 456 789]"
arr = str.split("\n").map(&:strip) # => ["[test| blah]", "[foo |bar bar bar]", "[test| abc |123 | 456 789]"]
arr = arr.map{|s| s[1..-2] } # => ["test| blah", "foo |bar bar bar", "test| abc |123 | 456 789"]
arr = arr.map{|s| s.split('|').map(&:strip)} # => [["test", "blah"], ["foo", "bar bar bar"], ["test", "abc", "123", "456 789"]]
This is likely far less efficient than scan, but at least it's simple :)
A "Scan, Split, Strip, and Delete" Train-Wreck
The whole premise seems flawed, since it assumes that you will always find alternation in your sub-arrays and that expressions won't contain character classes. Still, if that's the problem you really want to solve for, then this should do it.
First, str.scan( /\[.*?\]/ ) will net you three array elements, each containing pseudo-arrays. Then you map the sub-arrays, splitting on the alternation character. Each element of the sub-array is then stripped of whitespace, and the square brackets deleted. For example:
str = "[test| blah] \n [foo |bar bar bar]\n[test| abc |123 | 456 789]"
str.scan( /\[.*?\]/ ).map { |arr| arr.split('|').map { |m| m.strip.delete '[]' }}
#=> [["test", "blah"], ["foo", "bar bar bar"], ["test", "abc", "123", "456 789"]]
Verbosely, Step-by-Step
Mapping nested arrays is not always intuitive, so I've unwound the train-wreck above into more procedural code for comparison. The results are identical, but the following may be easier to reason about.
string = "[test| blah] \n [foo |bar bar bar]\n[test| abc |123 | 456 789]"
array_of_strings = string.scan( /\[.*?\]/ )
#=> ["[test| blah]", "[foo |bar bar bar]", "[test| abc |123 | 456 789]"]
sub_arrays = array_of_strings.map { |sub_array| sub_array.split('|') }
#=> [["[test", " blah]"],
# ["[foo ", "bar bar bar]"],
# ["[test", " abc ", "123 ", " 456 789]"]]
stripped_sub_arrays = sub_arrays.map { |sub_array| sub_array.map(&:strip) }
#=> [["[test", "blah]"],
# ["[foo", "bar bar bar]"],
# ["[test", "abc", "123", "456 789]"]]
sub_arrays_without_brackets =
stripped_sub_arrays.map { |sub_array| sub_array.map {|elem| elem.delete '[]'} }
#=> [["test", "blah"], ["foo", "bar bar bar"], ["test", "abc", "123", "456 789"]]

Ruby regex- matching text if it doesn't start with a character

I was wondering how you are able to match a pattern only if it doesn't start with a specific character. I would like to match "foo" but I would not like to match "afoo." What kind of regex operator do I need for that? I can't seem to find the right one. Maybe an anchor?
For example I'd like to change
foo foo afoo foo
to
bar bar afoo bar
Thanks.
Although the answer below is correct for my example, what about if it was /foo instead of afoo? That doesn't seem to behave the same?
Sounds like you're looking for a negative look behind. If you say (?<!expr1)expr2 then it will match whatever expr2 matches as long as it isn't immediately preceded by something expr1 matches. For example:
>> 'foo foo afoo foo'.gsub(/(?<!a)foo/, 'bar')
=> "bar bar afoo bar"
str = "foo foo afoo foo"
str.gsub(/\bfoo/, "bar") #=> "bar bar afoo bar"

Redmine plugin that replaces words via regular expression?

I'm very new to Redmine/Ruby trying to achieve a simple plugin that takes the current wiki page content and matches/replaces everytime a word occurs via regular expression. How can I do this?
Thanks!
Dennis
The word replacement can de done by using gsub() with \b to match a word boundary:
irb(main):001:0> 'foo bar baz foo bar'.gsub /\bfoo\b/, 'replaced'
=> "replaced bar baz replaced bar"
Here is a more complete solution with a dictionary of words to replace:
repl = {'foo'=>'apple', 'baz'=>'banana'}
s = 'foo bar baz foo bar'
for from, to in repl:
s = s.gsub /\b#{from}\b/, to
end
Result: apple bar banana apple bar

Resources