I have the following method in my Ruby model:
Old:
def to_s
numbers = self.title.scan(/\d+/) if self.title.scan(/\d+/)
return numbers.join.insert(0, "#{self.title.chop} ") if numbers
"#{self.title.titlecase}"
end
New:
def to_s
numbers = self.title.scan(/\d+/)
return numbers.join.insert(0, "#{self.title.sub(/\d+/, '')} ") if numbers.any?
self.title.titlecase
end
A title can be like so: Level1 or TrackStar
So TrackStar should become Track Star and Level1 should be come Level 1, which is why I am doing the scan for numbers to begin with
I am trying to display it like Level 1. The above works, I was just curious to know if there was a more eloquent solution
Try this:
def to_s
self.title.split(/(?=[0-9])/, 2).join(" ")
end
The second argument to split is to make sure a title like "Level10" doesn't get transformed into "Level 1 0".
Edit - to add spaces between words as well, I'd use gsub:
def to_s
self.title.gsub(/([a-z])([A-Z])/, '\1 \2').split(/(?=\d)/, 2).join(" ")
end
Be sure to use single-quotes in the second argument to gsub.
How about this:
'Level1'.split(/(\d+)/).join(' ')
#=> "Level 1"
Related
I'm trying to conditionally set a format of names with a "The" at the beginning. My regular expression works, but I don't know how to make it a conditional statement (basically if this record contains a "the" at the beginning, then "do splitting". As I'm showing a huge table of data, I'd like to keep this efficient.
Class Contact < ActiveRecord::Base
def adjusted_name
if "name LIKE ?", 'The%'
set_name = self.name.split('The ')[1]
self.name = format('%s, The', set_name)
end
end
end
gsub with a regex should do the trick:
def adjusted_name
self.name = name.gsub(/^The (.*)$/, '\1, The')
end
I am having trouble writing this so that it will take a sentence as an argument and perform the translation on each word without affecting the punctuation.
I'd also like to continue using the partition method.
It would be nice if I could have it keep a quote together as well, such as:
"I said this", I said.
would be:
"I aidsay histay", I said.
def convert_sentence_pig_latin(sentence)
p split_sentence = sentence.split(/\W/)
pig_latin_sentence = []
split_sentence.each do |word|
if word.match(/^[^aeiou]+/x)
pig_latin_sentence << word.partition(/^[^aeiou]+/x)[2] + word.partition(/^[^aeiou]+/x)[1] + "ay"
else
pig_latin_sentence << word
end
end
rejoined_pig_sentence = pig_latin_sentence.join(" ").downcase + "."
p rejoined_pig_sentence.capitalize
end
convert_sentence_pig_latin("Mary had a little lamb.")
Your main problem is that [^aeiou] matches every character outside that range, including spaces, commas, quotation marks, etc.
If I were you, I'd use a positive match for consonants, ie. [b-df-hj-np-tv-z] I would also put that regex in a variable, so you're not having to repeat it three times.
Also, in case you're interested, there's a way to make your convert_sentence_pig_latin method a single gsub and it will do the whole sentence in one pass.
Update
...because you asked...
sentence.gsub( /\b([b-df-hj-np-tv-z])(\w+)/i ) { "#{$2}#{$1}ay" }
# iterate over and replace regexp matches using gsub
def convert_sentence_pig_latin2(sentence)
r = /^[^aeiou]+/i
sentence.gsub(/"([^"]*)"/m) {|x| x.gsub(/\w+/) {|y| y =~ r ? "#{y.partition(r)[2]}#{y.partition(r)[1]}ay" : y}}
end
puts convert_sentence_pig_latin2('"I said this", I said.')
# define instance method: String#to_pl
class String
R = Regexp.new '^[^aeiou]+', true # => /^[^aeiou]+/i
def to_pl
self.gsub(/"([^"]*)"/m) {|x| x.gsub(/\w+/) {|y| y =~ R ? "#{y.partition(R)[2]}#{y.partition(R)[1]}ay" : y}}
end
end
puts '"I said this", I said.'.to_pl
sources:
http://www.ruby-doc.org/core-2.1.0/Regexp.html
http://ruby-doc.org/core-2.0/String.html#method-i-gsub
I need to generate a unique name based on a start_date of a model. I have the following code that's working. However, as you can see, it only supports up to letter Z. Is there a way that I can make it work for beyond Z, like this sequence? A,B,C,...X,Y,Z,AA,AB,AC,AD,AE...AZ,BA,BB,BC...
def generate_name
("A".."Z").each do |letter|
random_token = start_date.strftime("%m%d%y") + letter
break random_token unless Tour.where(name: random_token).exists?
end
end
def letters
('a'..'z').each { |i| yield i }
letters do |i|
('a'..'z').each { |j| yield i+j }
end
end
def generate_name
letters do |letter|
random_token = start_date.strftime("%m%d%y") + letter
break random_token unless Tour.where(name: random_token).exists?
end
end
Sam's answer looks like exactly what you requested but here's another possibly useful way to look at your problem:
Think of your string as a number in base 26, where the digits are represented by 'a'..'z'. The only difference between doing it this way and the way you requested is that the first two-digit number (err, two-letter string) will be ba rather than aa (assuming that you started counting with a = 0, b = 1, etc.) and you won't generate any strings starting with a (other than the first one) for the same reason you don't write whole numbers starting with 0 (other than the first one).
If it's useful for you to calculate very quickly that the name dw was the 100th one you generated, then this approach has merit. It's also a good way to work the word hexavigesimal into conversations or at least into documentation.
Thanks Sunxperous.
This worked:
def generate_name
("A".."ZZZ").each do |letter|
random_token = start_date.strftime("%m%d%y") + letter
break random_token unless Tour.where(name: random_token).exists?
end
end
You are looking for Kleene star. Not tested, but seems that RLTK gem can give you such functionality.
What's the best way in Ruby (with Rails, if relevant) to capitalize the first letter of a string?
Note that String#capitalize is not what I want since, in addition to capitalizing the first letter of the string, this function makes all other characters lowercase (which I don't want -- I'd like to leave them untouched):
>> "a A".capitalize
=> "A a"
In Rails you have the String#titleize method:
"testing string titleize method".titleize #=> "Testing String Titleize Method"
You can use "sub" to get what you want (note: I haven't tested this with multibyte strings)
"a A".sub(/^(\w)/) {|s| s.capitalize}
(and you can of course monkeypatch String to add this as a method if you like)
Upper case the first char, and save it back into the string
s = "a A"
s[0] = s[0,1].upcase
p s # => "A A"
Or,
class String
def ucfirst!
self[0] = self[0,1].upcase
self
end
end
If you don't want to modify the original string, you can do it this way:
class String
def ucfirst
str = self.clone
str[0] = str[0,1].upcase
str
end
end
I propose the following solution, works through whitespace
' ucfirstThis'.sub(/\w/, &:capitalize)
# => "UcfirstThis"
Since rails 5:
"a A".upcase_first
=> "A A"
http://api.rubyonrails.org/v5.1/classes/ActiveSupport/Inflector.html#method-i-upcase_first
If you are looking for a real similar function to PHPs ucfirst() try
"a A".gsub(/(\w+)/) {|s| s.capitalize}
will result in "A A".
"a neW APPROACH".gsub(/(\w+)/) {|s| s.capitalize}
will result in "A New Approach".
You can extend String class with:
class String
def ucfirst
self.gsub(/(\w+)/) { |s| s.capitalize }
end
def ucfirst!
self.gsub!(/(\w+)/) { |s| s.capitalize }
end
end
Have a look at this.
capitalizing-first-letter-of-each-word
There's not an inbuilt function. You need to split the letters and rejoin or try Rails' String#titleize and see if it does what you want.
That one liner does not depend on ActiveSupport. Not sure it's totally bulletproof though:
"my great uncle and grand-ma".gsub(/(\A\w|\s\w)/) { |m| m.upcase }
# My Great Uncle And Grand-ma
Is there a Ruby/Rails function that will strip a string of a certain user-defined character? For example if I wanted to strip my string of quotation marks "... text... "
http://api.rubyonrails.org/classes/ActiveSupport/Multibyte/Chars.html#M000942
I don't know if I'm reinventing the wheel here so if you find a built-in method that does the same, please let me know :-)
I added the following to config/initializers/string.rb , which add the trim, ltrim and rtrim methods to the String class.
# in config/initializers/string.rb
class String
def trim(str=nil)
return self.ltrim(str).rtrim(str)
end
def ltrim(str=nil)
if (!str)
return self.lstrip
else
escape = Regexp.escape(str)
end
return self.gsub(/^#{escape}+/, "")
end
def rtrim(str=nil)
if (!str)
return self.rstrip
else
escape = Regexp.escape(str)
end
return self.gsub(/#{escape}+$/, "")
end
end
and I use it like this:
"... hello ...".trim(".") => " hello "
and
"\"hello\"".trim("\"") => "hello"
I hope this helps :-)
You can use tr with the second argument as a blank string. For example:
%("... text... ").tr('"', '')
would remove all the double quotes.
Although if you are using this function to sanitize your input or output then it will probably not be effective at preventing SQL injection or Cross Site Scripting attacks. For HTML you are better off using the gem sanitize or the view helper function h.
I don't know of one out of the box, but this should do what you want:
class String
def strip_str(str)
gsub(/^#{str}|#{str}$/, '')
end
end
a = '"Hey, there are some extraneous quotes in this here "String"."'
puts a.strip_str('"') # -> Hey, there are some extraneous quotes in this here "String".
You could use String#gsub:
%("... text... ").gsub(/\A"+|"+\Z/,'')
class String
# Treats str as array of char
def stripc(str)
out = self.dup
while str.each_byte.any?{|c| c == out[0]}
out.slice! 0
end
while str.each_byte.any?{|c| c == out[-1]}
out.slice! -1
end
out
end
end
Chuck's answer needs some + signs if you want to remove all extra instances of his string pattern. And it doesn't work if you want to remove any of a set of characters that might appear in any order.
For instance, if we want a string to not end with any of the following: a, b, c, and our string is fooabacab, we need something stronger like the code I've supplied above.