Ruby replace only exact matching string with another string - ruby-on-rails

How can I solve following problem?
I have a html string like following one:
<p>aaa, b aa aaaaa?<br/>Next possible text b bb aa b.</p>
Now I'd like to replace for example only
"aaa"
with
"<div class='special'>aaa</div>"
new string after replace:
<p><div class='special'>aaa</div>, b aa aaaaa?<br/>Next possible text b bb aa b.</p>
So I´d like a generic replacer, which only replace an exact matching string. "aaa" was just an example. It could also be "bb" or "two words" (=> two words, so text.split won't work in my opinion).
Anybody an idea for such an dynamic find, match and replacer?
I tried it already like following:
items = ["aaa", "a", "aa", "aa b", "c"]
text = "<p>aaa, b aa aaaaa?<br/>Next possible text b bb aa b.</p>"
words = text.split => ["<p>aaa,", "b", "aa", "aaaaa?<br/>Next", "possible", "text", "b", "bb", "aa", "b.</p>"]
new_words = []
words.each do |w|
items.each do |item|
if w == item
w = '<div class="special">#{item}</div>'
end
end
new_words << w
end
text = new_words.join(" ")
Result:
"<p>aaa, b <div class='special'>aa</div> aaaaa?<br/>Next possible text b bb <div class='special'>aa</div> b.</p>"
But it should be:
"<p><div class='special'>aaa</div>, b <div class='special'>aa</div> aaaaa?<br/>Next possible text b bb <div class='special'>aa b</div>.</p>"
My biggest problems are:
Special characters like ",.?()%€"-characters at the end of a string
=> "aaa," from the example
Substrings with same parts => like "aaa" and "aa"
Two words as one item => like "aa b" from the example
Someone an idea for fixing my problems?
EDIT: something is only a placeholder for my replacement.. my real replacement could also be:
%Q( <dfn title="#{strip_tags item.text}">#{item.name}</dfn> )
item.text could be everything => could contain also "aaa"
item.name is for example "aaa"
So multiple gsub would replace also already replaced content.

It's not clear whether there is a single instance of aaa or multiple, and whether you want them all replaced, or just the first one.
This will replace just the first:
text = "<p>aaa, b aa aaaaa?<br/>Next possible text b bb aa b.</p>"
text.sub(/\b(aaa)\b/, %q"<div class='special'>\1</div>")
=> "<p><div class='special'>aaa</div>, b aa aaaaa?<br/>Next possible text b bb aa b.</p>"
This will replace all occurrences:
text = "<p>aaa, b aa aaaaa?<br/>Next possible text b bb aa b.</p>" * 2
=> "<p>aaa, b aa aaaaa?<br/>Next possible text b bb aa b.</p><p>aaa, b aa aaaaa?<br/>Next possible text b bb aa b.</p>"
text.gsub(/\b(aaa)\b/, %q"<div class='special'>\1</div>")
=> "<p><div class='special'>aaa</div>, b aa aaaaa?<br/>Next possible text b bb aa b.</p><p><div class='special'>aaa</div>, b aa aaaaa?<br/>Next possible text b bb aa b.</p>"
You can put "aaa" into a variable and find it by interpolating that target into the pattern:
target = 'aaa'
text.gsub(/\b(#{ target })\b/, %q"<div class='special'>\1</div>")
=> "<p><div class='special'>aaa</div>, b aa aaaaa?<br/>Next possible text b bb aa b.</p><p><div class='special'>aaa</div>, b aa aaaaa?<br/>Next possible text b bb aa b.</p>"
Regular expressions have the \b (word-break) marker, which makes it easy to define words, or substring, matches. You can replace "aaa" with multiple words also.

You're looking for String#sub (not gsub)
s = "<p>aaa, b aa aaaaa?<br/>Next possible text b bb aa b.</p>"
# => "<p>aaa, b aa aaaaa?<br/>Next possible text b bb aa b.</p>"
match = "aaa"
# => "aaa"
replacement = "<div class='special'>aaa</div>"
# => "<div class='special'>aaa</div>"
s.sub match, replacement
# => "<p><div class='special'>aaa</div>, b aa aaaaa?<br/>Next possible text b bb aa b.</p>"
"<p><div class='special'>aaa</div>, b aa aaaaa?<br/>Next possible text b bb aa b.</p>" == _
# => true

I would use a regular expression, Rubular is a great place to learn and test out your expressions. For more information on how to use gsub check out Jayfields Blog post. This may not fit all your use cases for this problem so you may need to modify it.
str.gsub /^<p>a{3}/, "<div class='special'>aaa</div>"
What this says is starting at the beginning of the string (^)
find <p> and 3 assurances of the letter a.

Here is the replacer method you wanted (using gsub, of course):
def replacer(orig,pattern,replace)
orig.gsub(/#{pattern}(\s|\.|,)/,replace+'\1').to_s
end
2.0.0dev :001 > def replacer(orig,pattern,replace)
2.0.0dev :002?> orig.gsub(/#{pattern}(\s|\.|,)/,replace+'\1').to_s
2.0.0dev :003?> end
=> nil
2.0.0dev :004 > replacer("<p>aaa, b aa aaaaa?<br/>Next possible text b bb aa b.</p>", "aaa", "<div class='special'>aaa</div>")
=> "<p><div class='special'>aaa</div>, b aa aaaaa?<br/>Next possible text b bb aa b.</p>"

Related

Rails: Allow only specific symbols and letters

Trying to write correct validation.
I have symbols/letters which can be only used:
ALLOWED_SYMBOLS = %w(/ # _ + - ( ) . , : ? ' & ; " 0 1 2 3 4 5 6 7 8 9 A a Ā ā Ą ą Ä ä B b C c Č č D d E e Ē ē Ę ę Ė ė F f G g Ģ ģ H h I i Ī ī Į į Y y J j K k Ķ ķ L l Ļ ļ M m N n Ņ ņ O o Õ õ Ö ö P p Q q R r S s Š š T t U u Ū ū Ü ü V v W w Z z Ž ž X x)
Afterwards i'm trying to validate it
validates :reference inclusion: { in: ALLOWED_SYMBOLS, allow_blank: true }
But it's not working properly as if i'm writing 2 or more allowed symbols i get an error, it does not happen when i type only 1 allowed one.
Looking forward for your help. The idea is to only allow mentioned symbols(they can be duplicated and such)
Rails 5.2
Your problem is that validating inclusion: {in: ...} is saying "the value must be one of the items in the list" -- which is not what you want at all.
Instead, what you're trying to check is "does reference only contain characters from this list?".
I would solve that with a regex. Suppose, for example, you wanted a string to only contain the characters a, b, c, d, ..., z. Then you could check the format with:
/\A[a-z]*\z/
Where \A means "start of string", \z means "end of string" and [a-z]* means "zero or more letters from the collection: a, b, c, ...., z".
However, your scenario is a little more awkward because you don't have a nice character set/range like [a-z]. You could write that very verbosely (but it's difficult to read/reuse the code):
/\A[\/#_+\-().,:?'&;"0123456789ĀāĄąÄäBb....]*\z/
Or, better, you could get a little fancy and convert the constant into a suitable regex:
/\A#{Regexp.union(*ALLOWED_SYMBOLS)}*\z/
And therefore your final answer is to replace this:
validates :reference inclusion: { in: ALLOWED_SYMBOLS, allow_blank: true }
With this:
validates :reference, format: { with: /\A#{Regexp.union(*ALLOWED_SYMBOLS)}*\z/ }
See also: https://guides.rubyonrails.org/active_record_validations.html

How do I replace each substring in a string?

I have a string:
story = 'A long foo ago, in a foo bar baz, baz away...foobar'
I also have matches from this string (the dictionary is dynamic, it doesn't depend on me)
string_matches = ['foo', 'foo', 'bar', 'baz', 'baz', 'foobar'] # words can be repeated
How to replace each match with **foo**? to get a result:
story = 'A long **foo** ago, in a **foo** **bar** **baz**, **baz** away...**foobar**'
for example my code:
string_matches.each do |word|
story.gsub!(/#{word}/, "**#{word}**")
end
returned:
"A long ****foo**** ago, in a ****foo**** **bar** ****baz****, ****baz**** away...****foo******bar**"
If you need to check if the words are matched as whole words, you may use
story.gsub(/\b(?:#{Regexp.union(string_matches.uniq.sort { |a,b| b.length <=> a.length }).source})\b/, '**\0**')
If the whole word check is not necessary use
story.gsub(Regexp.union(string_matches.uniq.sort { |a,b| b.length <=> a.length }), '**\0**')
See the Ruby demo
Details
\b - a word boundary
(?:#{Regexp.union(string_matches.uniq.sort { |a,b| b.length <=> a.length }).source}) - this creates a pattern like (?:foobar|foo|bar|baz) that matches a single word from the deduplicated list of keywords, and sorts them by length in the descending order. See Order of regular expression operator (..|.. ... ..|..) why this is necessary.
\b - a word boundary
The \0 in the replacement pattern is the replacement backreference referring to the whole match.
A slight change will nearly get you there:
irb(main):001:0> string_matches.uniq.each { |word| story.gsub!(/#{word}/, "**#{word}**") }
=> ["foo", "bar", "baz", "foobar"]
irb(main):002:0> story
=> "A long **foo** ago, in a **foo** **bar** **baz**, **baz** away...**foo****bar**"
The trouble with the final part of the resulting string is that foobar has been matched by both foo and foobar.

Method to order list with dependancies

Conditions: Have to use web based solution (HTML/CSS), Have to use Ruby on Rails, no use of database.
Imagine we have a list of jobs, each represented by a character. Because certain jobs must be done before others, a job may have a dependency on another job. For example, a may depend on b, meaning the final sequence of jobs should place b before a. If a has no dependency, the position of a in the final sequence does not matter. These jobs would be input with a simple text box (also how does one store multiple variables)
Given the following job structure:
a =>
b =>
c =>
The result should be a sequence containing all three jobs abc in no significant order.
Given the following job structure:
a =>
b => c
c => f
d => a
e => b
f =>
The result should be a sequence that positions f before c, c before b, b before e and a before d containing all six jobs abcdef.
Given the following job structure:
a =>
b => c
c => f
d => a
e =>
f => b
The result should be an error stating that jobs can’t have circular dependencies.
This should work:
module JobDependenciesResolver
def self.organize(dependencies)
unresolved_dependencies = dependencies.dup
sorted_jobs = []
until unresolved_dependencies.empty? do
doable_jobs = get_doable_jobs unresolved_dependencies, sorted_jobs
raise Exception.new("Not able to resolve dependencies") if doable_jobs.empty?
sorted_jobs += doable_jobs
unresolved_dependencies.delete_if {|key,value| sorted_jobs.include? key}
end
sorted_jobs
end
private
def self.get_doable_jobs(dependencies, job_array)
dependencies.select {|job, dependency| ([*dependency]-job_array).empty? }.keys
end
end

How to check if a `Integer` is greater than another `Integer` "easily" / "efficiently"?

I am using Ruby on Rails 3.2.2 and I would like to check if a Integer is greater than 0 and, more in general, if a Integer is greater than another Integer.
There is some Ruby or Ruby on Rails method to make that "easily" / "efficiently"?
Note: I would like to use / state that method in my view files and I think, if that method do not "exist", it could be better to state a "dedicated" method in my model or controller file and use that method in my views.
Whenever I start comparing more than two integers, I usually revert to array#max.
a = 1
b = 2
[0, a, b].max == a # false
a = 3
[0, a, b].max == a # true
The primary weakness of this is if a == b, so a special check is required for that case. Or you can do:
[0, a, b + 1].max == a
or
[0, a, b].max == a && a != b
EDIT:
This method would probably fit best in your helpers.
As shown here:
a = (print "enter a value for a: "; gets).to_i
b = (print "enter a value for b: "; gets).to_i
puts "#{a} is less than #{b}" if a < b
puts "#{a} is greater than #{b}" if a > b
puts "#{a} is equal to #{b}" if a == b
You can use standard Ruby within your views between <% and %>. And yes, you could implement a helper do to the check and use that helper method in your view.

How to split dot "." only before equal "=" in Ruby

I need to split a string at a period that comes before an equal sign to assign to a hash. E.g.,
"Project.risksPotentialAfterSum=Pot. aft."
should be splitted like this:
{"Project" =>{"risksPotentialAfterSum" => "Pot. aft."}}
For now, I use str.split(/[\.=]/,2) which has a problem for the value that comes after the equal sign. Any ideas?
str = "Project.risksPotentialAfterSum=Pot. aft."
m = str.match(/\A(?<obj>.+?)\.(?<prop>[^.]+?)=(?<val>.+)/)
#=> #<MatchData "Project.risksPotentialAfterSum=Pot. aft." obj:"Project"
h = { m[:obj]=>{ m[:prop]=>m[:val] } }
#=> {"Project"=>{"risksPotentialAfterSum"=>"Pot. aft."}}
That regex says, roughly:
Starting at the start of the string,
find just about anything on the same line (name it 'obj') up until you see a period,
that is followed by one or more characters that aren't a period (name it 'prop') up until you see an equals sign,
and name whatever comes after the equals sign 'val'.
ruby-1.9.2-p136 :028 > str
=> "Project.risksPotentialAfterSum=Pot. aft."
ruby-1.9.2-p136 :029 > split = str.split(/\.|=/,3)
=> ["Project", "risksPotentialAfterSum", "Pot. aft."]
ruby-1.9.2-p136 :030 > Hash[*[split[0],Hash[*split[1,2]]]]
=> {"Project"=>{"risksPotentialAfterSum"=>"Pot. aft."}}
Concepts used here:
Uitlizing the | for regex with states: match the left or match the right of |.
Using the splat operator
Create hash based on list.
Instead of using string splitting you could consider using regular expression matching and capturing the values that you're interested in.
m = "Project.risksPotentialAfterSum=Pot. aft.".match /(\w+)\.(\w+)=(.*)/
h = {m[1] => {m[2] => m[3]}}
#=> {"Project"=>{"risksPotentialAfterSum"=>"Pot. aft."}}

Resources