What's the best way to get the first part of a string and remove the second part in Ruby ?
I know the first part to keep for each case
Examples:
"The Cuvée titi" => "The Cuvée"
"The Cuvée toto" => "The Cuvée"
"The Cuvée toto 1234" => "The Cuvée"
"1234 The Cuvée" => need to do nothing
"The wine 45 67" => "The wine"
"The wine not good" => " The wine"
"What's The wine ?" => need to do nothing
I tried many things, after reading some discussions, including:
sub(), delete() => keep the second part, not the first
even delete_suffix() of ruby 2.5 => not working in some cases
string[0..x] => but works also when the start of the string is not good
Is a regex required ?
Thanks
I don't quite understand the problem.
If you know what the first part of the string is, and you only want to keep the first part of the string, there is no need to do anything, because you already know what the first part of the string is.
if string.start_with?(string_to_keep)
string_to_keep
end
Well, this is what I did:
if string.start_with? string_to_keep
new_string = string[0.. string_to_keep.length-1]
end
Surely not the best Ruby way, but working...
key_string = "The Cuvée"
example_string_1 = "The Cuvée toto 1234"
example_string_2 = "1234 The Cuvée"
index will return the first occurrence of the key string in your examples. You can then return everything including the first occurrence of your key string with slice, like:
result1 = example_string_1.slice(0, example_string_1.index(key_string) + key_string.length)
=> "The Cuvée"
result2 = example_string_2.slice(0, example_string_2.index(key_string) + key_string.length)
=> "1234 The Cuvée"
It appears to me that the objective could be viewed as removing the portion of the string that follows the given string.
def doit(str, given_str)
str.gsub(/(?<=\b#{given_str}\b).*/, '')
end
given_str = "The Cuvée"
doit "The Cuvée titi", given_str #=> "The Cuvée"
doit "The Cuvée toto", given_str #=> "The Cuvée"
doit "The Cuvée toto 1234", given_str #=> "The Cuvée"
doit "1234 The Cuvée", given_str #=> "1234 The Cuvée"
given_str = "The wine"
doit "The wine 45 67", given_str #=> "The wine"
doit "The wine not good", given_str #=> "The wine"
doit "What's The wine?", given_str #=> "What's The wine"
Note the question mark is removed in the last example.
For
given_str = "The wine"
the regular expression is
/(?<=\b#{given_str}\b).*/
#=> /(?<=\bThe wine\b).*/
(?<=\bThe wine\b) is a positive lookbehind, meaning the the match is preceded by "The wine", with word breaks on either side. .* matches zero or more characters, as many as possible, meaning the remaining characters in the string.
One more example for given_str = "The wine":
doit "Go to The winery for The wine", given_str
#=> "Go to The winery for The wine"
Without the word breaks (\b) this would be:
doit "Go to The winery for The wine", given_str
#=> "Go to The wine"
The question does not tell me which result is desired.
Related
How to replace a file in Ruby, but do not touch commented-out lines? To be more specific I want to change variable in configuration file. An example would be:
irb(main):014:0> string = "#replaceme\n\t\s\t\s# replaceme\nreplaceme\n"
=> "#replaceme\n\t \t # replaceme\nreplaceme\n"
irb(main):015:0> puts string.gsub(%r{replaceme}, 'replaced')
#replaced
# replaced
replaced
=> nil
irb(main):016:0>
Desired output:
#replaceme
# replaceme
replaced
I don't fully understand the question. To do a find and replace in each line, disregarding text following a pound sign, one could do the following.
def replace_em(str, source, replacement)
str.split(/(\#.*?$)/).
map { |s| s[0] == '#' ? s : s.gsub(source, replacement) }.
join
end
str = "It was known that # that dog has fleas, \nbut who'd know that that dog # wouldn't?"
replace_em(str, "that", "the")
#=> "It was known the # that dog has fleas, \nbut who'd know the the dog # wouldn't?"
str = "#replaceme\n\t\s\t\s# replaceme\nreplaceme\n"
replace_em(str, "replaceme", "replaced")
#=> "#replaceme\n\t \t # replaceme\nreplaced\n"
For the string
str = "It was known that # that dog has fleas, \nbut who'd know that that dog # wouldn't?"
source = "that"
replacement = "the"
the steps are as follows.
a = str.split(/(\#.*?$)/)
#=> ["It was known that ", "# that dog has fleas, ",
# "\nbut who'd know that that dog ", "# wouldn't?"]
Note that the body of the regular expression must be put in a capture group in order that the text used to split the string be included as elements in the resulting array. See String#split.
b = a.map { |s| s[0] == '#' ? s : s.gsub(source, replacement) }
#=> ["It was known the ", "# that dog has fleas, ",
# "\nbut who'd know the the dog ", "# wouldn't?"]
b.join
#=> "It was known the # that dog has fleas, \nbut who'd know the the dog # wouldn't?"
How about this?
puts string.gsub(%r{^replaceme}, 'replaced')
This question already has an answer here:
Reference - What does this regex mean?
(1 answer)
Closed 5 years ago.
I have a string:
"1 chocolate bar at 25"
and I want to split this string into:
[1, "chocolate bar", 25]
I don't know how to write a regex for this split. And I wanted to know whether there are any other functions to accomplish it.
You could use scan with a regex:
"1 chocolate bar at 25".scan(/^(\d+) ([\w ]+) at (\d+)$/).first
The above method doesn't work if item_name has special characters.
If you want a more robust solution, you can use split:
number1, *words, at, number2 = "1 chocolate bar at 25".split
p [number1, words.join(' '), number2]
# ["1", "chocolate bar", "25"]
number1 is the first part, number2 is the last one, at the second to last, and *words is an array with everything in-between. number2 is guaranteed to be the last word.
This method has the advantage of working even if there are numbers in the middle, " at " somewhere in the string or if prices are given as floats.
It is not necessary to use a regular expression.
str = "1 chocolate bar, 3 donuts and a 7up at 25"
i1 = str.index(' ')
#=> 1
i2 = str.rindex(' at ')
#=> 35
[str[0,i1].to_i, str[i1+1..i2-1], str[i2+3..-1].to_i]
#=> [1, "chocolate bar, 3 donuts and a 7up", 25]
I would do:
> s="1 chocolate bar at 25"
> s.scan(/[\d ]+|[[:alpha:] ]+/)
=> ["1 ", "chocolate bar at ", "25"]
Then to get the integers and the stripped string:
> s.scan(/[\d ]+|[[:alpha:] ]+/).map {|s| Integer(s) rescue s.strip}
=> [1, "chocolate bar at", 25]
And to remove the " at":
> s.scan(/[\d ]+|[[:alpha:] ]+/).map {|s| Integer(s) rescue s[/.*(?=\s+at\s*)/]}
=> [1, "chocolate bar", 25]
You may try returning captures property of match method on regex (\d+) ([\w ]+) at (\d+):
string.match(/(\d+) +(\D+) +at +(\d+)/).captures
Live demo
Validating input string
If you didn't validate your input string to be within desired format already, then there may be a better approach in validating and capturing data. This solution also brings the idea of accepting any type of character in item_name field and decimal prices at the end:
string.match(/^(\d+) +(.*) +at +(\d+(?:\.\d+)?)$/).captures
You can also do something like this:
"1 chocolate bar at 25"
.split()
.reject {|string| string == "at" }
.map {|string| string.scan(/^\D+$/).empty? ? string.to_i : string }
Code Example: http://ideone.com/s8OvlC
I live in the country where prices might be float, hence the more sophisticated matcher for the price.
"1 chocolate bar at 25".
match(/\A(\d+)\s+(.*?)\s+at\s+(\d[.\d]*)\z/).
captures
#⇒ ["1", "chocolate bar", "25"]
How do I get the .include? to work? When the user chooses a character, I want the console to print the puts ok statement and then go to the if statement.
name = {"1" => "Mario",
"2" => "Luigi",
"3" => "Kirby",
}
puts "Peach's apocalypse, will you survive?"
def character (prompt, options)
puts = "who will you be?"
options = name[1] || name[2] || name[3]
character = gets.chomp.downcase
until character.include? name
end
puts "ok #{name} all three of you run out of peach's castle which has been overrun"
if character = name[1] || name[2] || name[3]
puts ("zombies are in the castle grounds, there are weapons over the bridge")
puts "What do you do, charge through or sneak?"
x = gets.chomp.downcase
if x == "sneak"
puts "oh you died"
if x == "charge through"
puts "the zombies tumbled over the bridge's edge, you made it safe and sound"
else
puts "you did nothing and were eaten alive by Princess Peach"
end
end
end
end
It looks like you're calling include? on a string. This will only return true if you pass it a substring of itself. For example:
"Mario".include?("Mar") #=> true
You want to call include? on the array of keys in the name hash. You could do:
name.values.include?(character)
or more concisely
name.has_value?(character)
Here's some documentation on the include? method of the Array class and the include? method of the string class, as well as the has_value? method of the Hash class.
There's considerably more that needs modifying for this program to run as you're expecting it to though. Here's one working implementation:
puts "Peach's apocalypse, will you survive?"
names = {
"1" => "Mario",
"2" => "Luigi",
"3" => "Kirby"
}
def choose_character(character = "", options)
puts = "who will you be?"
options.each do |num, name|
puts "#{num}: #{name}"
end
until options.has_key? character or options.has_value? character
character = gets.chomp.capitalize
end
return options[character] || character
end
name = choose_character(names)
puts "ok #{name} all three of you run out of peach's castle which has been overrun"
puts "zombies are in the castle grounds, there are weapons over the bridge"
puts "What do you do, charge through or sneak?"
case gets.chomp.downcase
when "sneak"
puts "oh you died"
when "charge through"
puts "the zombies tumbled over the bridge's edge, you made it safe and sound"
else
puts "you did nothing and were eaten alive by Princess Peach"
end
The answer above is great and features awesome refactoring, but I would use
character = gets.strip.downcase
instead as it also gets rid of any potential whitespace.
To elaborate on the string thing, 'gets' stands for 'get string' (or at least so I was taught), so everything you get via 'gets' will be a string until you convert it further. Consider this:
2.2.1 :001 > puts "put in your input"
put in your input
=> nil
2.2.1 :002 > input = gets.strip
5
=> "5"
2.2.1 :003 > input.class
=> String
You would have to use .to_i to convert your input back to integer.
How can I replace characters from string without changing its object_id?
For example:
string = "this is a test"
The first 7 characters need to be replaced with capitalized characters like: "THIS IS a Test" and the object_id needs to be the same. In which way can I sub or replace the characters to make it happen?
You can do it like this:
string = "this is a test"
string[0, 7] = string[0, 7].upcase
With procedural languages, one might write the equivalent of:
string = "this is in jest"
string.object_id
#=> 70309969974760
(1..7).each { |i| string[i] = string[i].upcase }
#=> 1..7
string
#=> "tHIS IS in jest"
string.object_id
#=> 70309969974760
This is not very Ruby-like, but it does offer the advantage over #sawa's solution that it does not create a temporary 7-character string. (Well, it does create a one-character string.) This is unimportant for strings of reasonable length (and for those I'd certainly concur with sawa), but it could be significant for really, really, really long strings.
Another way to do this is as follows:
string.each_char.with_index { |c,i|
string[i] = string[i].upcase if (1..7).cover?(i) }
#=> "tHIS IS in jest"
string.object_id
#=> 70309969974760
This second way might be more efficient if string is not much larger than string[start_index..end_index].
Edit:
In a comment the OP indicates that the string is to be stripped, squeeze and reversed as well as certain characters converted to upper case. That could be done on the string in place, without creating a copy, as follows:
def strip_upcase_squeeze_reverse_whew(string, upcase_range, squeeze_str=nil)
string.strip!
upcase_range.each { |i| string[i] = string[i].upcase }
squeeze_str.nil? ? string.squeeze! : string.squeeze!(squeeze_str)
string.reverse!
end
I have assumed the four operations would be performed in a particular order, but if the order should be different, that's an easy fix.
string = " this may bee inn jest, butt it's alsoo a test "
string.object_id
#=> 70309970103280
strip_upcase_squeeze_reverse_whew(string, (1..7))
#=> "tset a osla s'ti tub ,tsej ni eb YAM SIHt"
string.object_id
#=> 70309970103280
The steps:
string = "this may bee inn jest, butt it's alsoo a test"
#=> "this may bee inn jest, butt it's alsoo a test"
upcase_range = (1..7)
#=> 1..7
string.strip!
#=> nil
string
#=> "this may bee inn jest, butt it's alsoo a test"
upcase_range.each { |i| string[i] = string[i].upcase }
#=> 1..7
string
#=> "tHIS MAY bee inn jest, butt it's alsoo a test"
squeeze_str.nil? ? string.squeeze! : string.squeeze!(squeeze_str)
#=> "tHIS MAY be in jest, but it's also a test"
string
#=> "tHIS MAY be in jest, but it's also a test"
string.reverse!
#=> "tset a osla s'ti tub ,tsej ni eb YAM SIHt"
Notice that in this example, strip! does not remove any characters, and therefore returns nil. Similarly, squeeze! would return nil if there is nothing to squeeze. It is for that reason that strip! and squeeze cannot be chained.
A second example:
string = " thiiiis may beeee in jeeest"
strip_upcase_squeeze_reverse_whew(string, (12..14), "aeiouAEIOU")
Adding onto a string without changing its object id:
foo = "foo"
# => "foo"
foo.object_id
# => 70196045363960
foo << "bar"
# => "foobar"
foo.object_id
# => 70196045363960
Replace an entire string without changing its object id
foo
# => "foo"
foo.object_id
# => 70196045363960
foo.gsub!(/./, '') << 'bar'
# => 'bar'
foo.object_id
# => 70196045363960
Replace part of a string without changing its object id
foo
# => "foo"
foo.object_id
# => 70196045363960
foo.gsub!(/o/, 'z')
# => 'fzz'
foo.object_id
# => 70196045363960
Such as in the following code from Why's Poignant Guide:
def wipe_mutterings_from( sentence )
unless sentence.respond_to? :include?
raise ArgumentError, "cannot wipe mutterings from a #{ sentence.class }"
end
while sentence.include? '('
open = sentence.index( '(' )
close = sentence.index( ')', open )
sentence[open..close] = '' if close
end
end
In a Ruby double-quoted string—which includes string literals like s = "…" and s = %Q{ ... } and s = <<ENDCODE—the syntax #{ … } is used for "string interpolation", inserting dynamic content into the string. For example:
i = 42
s = "I have #{ i } cats!"
#=> "I have 42 cats!"
It is equivalent to (but more convenient and efficient than) using string concatenation along with explicit calls to to_s:
i = 42
s= "I have " + i.to_s + " cats!"
#=> "I have 42 cats!"
You can place arbitrary code inside the region, including multiple expressions on multiple lines. The final result of evaluating the code has to_s called on it to ensure that it is a string value:
"I've seen #{
i = 10
5.times{ i+=1 }
i*2
} weasels in my life"
#=> "I've seen 30 weasels in my life"
[4,3,2,1,"no"].each do |legs|
puts "The frog has #{legs} leg#{:s if legs!=1}"
end
#=> The frog has 4 legs
#=> The frog has 3 legs
#=> The frog has 2 legs
#=> The frog has 1 leg
#=> The frog has no legs
Note that this has no effect inside single-quoted strings:
s = "The answer is #{6*7}" #=> "The answer is 42"
s = 'The answer is #{6*7}' #=> "The answer is #{6*7}"
s = %Q[The answer is #{ 6*7 }] #=> "The answer is 42"
s = %q[The answer is #{ 6*7 }] #=> "The answer is #{6*7}"
s = <<ENDSTRING
The answer is #{6*7}
ENDSTRING
#=> "The answer is 42\n"
s = <<'ENDSTRING'
The answer is #{6*7}
ENDSTRING
#=> "The answer is #{6*7}\n"
For convenience, the {} characters for string interpolation are optional if you want to insert just the value of an instance variable (#foo), global variable ($foo), or class variable (##foo):
#cats = 17
s1 = "There are #{#cats} cats" #=> "There are 17 cats"
s2 = "There are ##cats cats" #=> "There are 17 cats"
"#{}" means in Ruby string interpolation.See Here for too many answers.
#{} is used for Ruby interpolation. In this example,
this will raise an ArgumentError with the message,
cannot wipe mutterings from a <whatever sentence.class evaluates to>
This is a useful read - String concatenation vs. interpolation in Ruby