Hi I'm building a function that has me take any instance of "u" or "you" in a string and replace it with a specific word. I can go in and isolate the instances no problem but I cannot get the words to output properly. So far I have.
def autocorrect(input)
#replace = [['you','u'], ['your sister']]
#replace.each{|replaced| input.gsub!(replaced[0], replaced[1])}
input.split(" ")
if (input == "u" && input.length == 1) || input == "you"
input.replace("your sister")
end
input.join(" ")
end
The ideal output would be:
autocorrect("I am so smitten with you")
"I am smitten with your sister"
I don't know how to get the last part correct, I can't think of a good method to use. Any help would be greatly appreciated.
The problem you're having with your code is that you call input.split(" ") but you don't save that to anything, and then you check for input == "u" # ..., and input is still the entire string, so if you called autocorrect('u') or autocorrect('you') you would get "your sister" back, except for the next line: input.join(" ") will throw an error.
This error is because, remember input is still the original string, not an array of each word, and strings don't have a join method.
To get your code working with the fewest changes possible, you can change it to:
def autocorrect(input)
#replace = [['you','u'], ['your sister']]
#replace.each{|replaced| input.gsub!(replaced[0], replaced[1])}
input.split(" ").map do |word|
if (word == "u" && word.length == 1) || word == "you"
"your sister"
else
word
end
end.join(" ")
end
So, now, you are doing something with each word after you split(" ") the input, and you are checking each word against "u" and "you", instead of the entire input string. You then map either the replacement word, or the original, and then join them back into a single string to return them.
As an alternative, shorter way, you can use String#gsub which can take a Hash as the second parameter to do substitutions:
If the second argument is a Hash, and the matched text is one of its keys, the corresponding value is the replacement string.
def autocorrect(input)
replace = { 'you' => 'your sister',
'u' => 'your sister',
'another word' => 'something else entirely' }
input.gsub(/\b(#{replace.keys.join('|')})\b/, replace)
end
autocorrect("I am u so smitten with utopia you and another word")
# => "I am your sister so smitten with utopia your sister and something else entirely"
the regex in that example comes out looking like:
/\b(you|u|another word)\b/
with \b being any word boundary.
Simple array mapping would do the job:
"I am u so smuitten with utopia you".split(' ').map{|word| %w(you u).include?(word) ? 'your sister' : word}.join(' ')
#=> "I am your sister so smuitten with utopia your sister"
Your method would be:
def autocorrect(input)
input.split(' ').map{|word| %w(you u).include?(word) ? 'your sister' : word}.join(' ')
end
autocorrect("I am so smitten with you")
#=> "I am smitten with your sister"
Related
My purpose is to accept a paragraph of text and find the specified phrase I want to REDACT, or replace.
I made a method that accepts an argument as a string of text. I break down that string into individual characters. Those characters are compared, and if they match, I replace those characters with *.
def search_redact(text)
str = ""
print "What is the word you would like to redact?"
redacted_name = gets.chomp
puts "Desired word to be REDACTED #{redacted_name}! "
#splits name to be redacted, and the text argument into char arrays
redact = redacted_name.split("")
words = text.split("")
#takes char arrays, two loops, compares each character, if they match it
#subs that character out for an asterisks
redact.each do |x|
if words.each do |y|
x == y
y.gsub!(x, '*') # sub redact char with astericks if matches words text
end # end loop for words y
end # end if statment
end # end loop for redact x
# this adds char array to a string so more readable
words.each do |z|
str += z
end
# prints it out so we can see, and returns it to method
print str
return str
end
# calling method with test case
search_redact("thisisapassword")
#current issues stands, needs to erase only if those STRING of characters are
# together and not just anywehre in the document
If I put in a phrase that shares characters with others parts of the text, for example, if I call:
search_redact("thisisapassword")
then it will replace that text too. When it accepts input from the user, I want to get rid of only the text password. But it then looks like this:
thi*i**********
Please help.
This is a classic windowing problem used to find a substring in a string. There are many ways to solve this, some that are much more efficient than others but I'm going to give you a simple one to look at that uses as much of your original code as possible:
def search_redact(text)
str = ""
print "What is the word you would like to redact?"
redacted_name = gets.chomp
puts "Desired word to be REDACTED #{redacted_name}! "
redacted_name = "password"
#splits name to be redacted, and the text argument into char arrays
redact = redacted_name.split("")
words = text.split("")
words.each.with_index do |letter, i|
# use windowing to look for exact matches
if words[i..redact.length + i] == redact
words[i..redact.length + i].each.with_index do |_, j|
# change the letter to an astrisk
words[i + j] = "*"
end
end
end
words.join
end
# calling method with test case
search_redact("thisisapassword")
The idea here is we're taking advantage of array == which allows us to say ["a", "b", "c"] == ["a", "b", "c"]. So now we just walk the input and ask does this sub array equal this other sub array. If they do match, we know we need to change the value so we loop through each element and replace it with a *.
I'm attempting to write a function that takes a string and returns it with all vowels removed. Below is my code.
def vowel(str)
result = ""
new = str.split(" ")
i = 0
while i < new.length
if new[i] == "a"
i = i + 1
elsif new[i] != "a"
result = new[i] + result
end
i = i + 1
end
return result
end
When I run the code, it returns the exact string that I entered for (str). For example, if I enter "apple", it returns "apple".
This was my original code. It had the same result.
def vowel(str)
result = ""
new = str.split(" ")
i = 0
while i < new.length
if new[i] != "a"
result = new[i] + result
end
i = i + 1
end
return result
end
I need to know what I am doing wrong using this methodology. What am I doing wrong?
Finding the bug
Let's see what's wrong with your original code by executing your method's code in IRB:
$ irb
irb(main):001:0> str = "apple"
#=> "apple"
irb(main):002:0> new = str.split(" ")
#=> ["apple"]
Bingo! ["apple"] is not the expected result. What does the documentation for String#split say?
split(pattern=$;, [limit]) → anArray
Divides str into substrings based on a delimiter, returning an array of these substrings.
If pattern is a String, then its contents are used as the delimiter when splitting str. If pattern is a single space, str is split on whitespace, with leading whitespace and runs of contiguous whitespace characters ignored.
Our pattern is a single space, so split returns an array of words. This is definitely not what we want. To get the desired result, i.e. an array of characters, we could pass an empty string as the pattern:
irb(main):003:0> new = str.split("")
#=> ["a", "p", "p", "l", "e"]
"split on empty string" feels a bit hacky and indeed there's another method that does exactly what we want: String#chars
chars → an_array
Returns an array of characters in str. This is a shorthand for str.each_char.to_a.
Let's give it a try:
irb(main):004:0> new = str.chars
#=> ["a", "p", "p", "l", "e"]
Perfect, just as advertised.
Another bug
With the new method in place, your code still doesn't return the expected result (I'm going to omit the IRB prompt from now on):
vowel("apple") #=> "elpp"
This is because
result = new[i] + result
prepends the character to the result string. To append it, we have to write
result = result + new[i]
Or even better, use the append method String#<<:
result << new[i]
Let's try it:
def vowel(str)
result = ""
new = str.chars
i = 0
while i < new.length
if new[i] != "a"
result << new[i]
end
i = i + 1
end
return result
end
vowel("apple") #=> "pple"
That looks good, "a" has been removed ("e" is still there, because you only check for "a").
Now for some refactoring.
Removing the explicit loop counter
Instead of a while loop with an explicit loop counter, it's more idiomatic to use something like Integer#times:
new.length.times do |i|
# ...
end
or Range#each:
(0...new.length).each do |i|
# ...
end
or Array#each_index:
new.each_index do |i|
# ...
end
Let's apply the latter:
def vowel(str)
result = ""
new = str.chars
new.each_index do |i|
if new[i] != "a"
result << new[i]
end
end
return result
end
Much better. We don't have to worry about initializing the loop counter (i = 0) or incrementing it (i = i + 1) any more.
Avoiding character indices
Instead of iterating over the character indices via each_index:
new.each_index do |i|
if new[i] != "a"
result << new[i]
end
end
we can iterate over the characters themselves using Array#each:
new.each do |char|
if char != "a"
result << char
end
end
Removing the character array
We don't even have to create the new character array. Remember the documentation for chars?
This is a shorthand for str.each_char.to_a.
String#each_char passes each character to the given block:
def vowel(str)
result = ""
str.each_char do |char|
if char != "a"
result << char
end
end
return result
end
The return keyword is optional. We could just write result instead of return result, because a method's return value is the last expression that was evaluated.
Removing the explicit string
Ruby even allows you to pass an object into the loop using Enumerator#with_object, thus eliminating the explicit result string:
def vowel(str)
str.each_char.with_object("") do |char, result|
if char != "a"
result << char
end
end
end
with_object passes "" into the block as result and returns it (after the characters have been appended within the block). It is also the last expression in the method, i.e. its return value.
You could also use if as a modifier, i.e.:
result << char if char != "a"
Alternatives
There are many different ways to remove characters from a string.
Another approach is to filter out the vowel characters using Enumerable#reject (it returns a new array containing the remaining characters) and then join the characters (see Nathan's answer for a version to remove all vowels):
def vowel(str)
str.each_char.reject { |char| char == "a" }.join
end
For basic operations like string manipulation however, Ruby usually already provides a method. Check out the other answers for built-in alternatives:
str.delete('aeiouAEIOU') as shown in Gagan Gami's answer
str.tr('aeiouAEIOU', '') as shown in Cary Swoveland's answer
str.gsub(/[aeiou]/i, '') as shown in Avinash Raj's answer
Naming things
Cary Swoveland pointed out that vowel is not the best name for your method. Choose the names for your methods, variables and classes carefully. It's desirable to have a short and succinct method name, but it should also communicate its intent.
vowel(str) obviously has something to do with vowels, but it's not clear what it is. Does it return a vowel or all vowels from str? Does it check whether str is a vowel or contains a vowel?
remove_vowels or delete_vowels would probably be a better choice.
Same for variables: new is an array of characters. Why not call it characters (or chars if space is an issue)?
Bottom line: read the fine manual and get to know your tools. Most of the time, an IRB session is all you need to debug your code.
I should use regex.
str.gsub(/[aeiou]/i, "")
> string= "This Is my sAmple tExt to removE vowels"
#=> "This Is my sAmple tExt to removE vowels"
> string.delete 'aeiouAEIOU'
#=> "Ths s my smpl txt t rmv vwls"
You can create a method like this:
def remove_vowel(str)
result = str.delete 'aeiouAEIOU'
return result
end
remove_vowel("Hello World, This is my sample text")
# output : "Hll Wrld, Ths s my smpl txt"
Live Demo
Assuming you're trying to learn about the basics of programming, rather than finding the quickest one-liner to do this (which would be to use a regular expression as Avinash has said), you have a number of problems with your code you need to change.
new = str.split(" ")
This line is likely the culprit, because it splits the string based on spaces. So your input string would have to be "a p p l e" to have the effect you're looking for.
new = str.split("")
You should also remove the duplicate i = i+1 once you've changed that.
As others have already identified the problems with the OP's code, I will merely suggest an alternative; namely, you could use String#tr:
"Now is the time for all good people...".tr('aeiouAEIOU', '')
#=> "Nw s th tm fr ll gd ppl..."
If regex is not allowed, you can do it this way:
def remove_vowels(string)
string.split("").delete_if { |letter| %w[a e i o u].include? letter }.join
end
When looping through lines of text, what is the neatest way (most 'Ruby') to do an if else statement (or similar) to check if the string is a single word or not?
def check_if_single_word(string)
# code here
end
s1 = "two words"
s2 = "hello"
check_if_single_word(s1) -> false
check_if_single_word(s2) -> true
Since you're asking about the 'most Ruby' way, I'd rename the method to single_word?
One way is to check for the presence of a space character.
def single_word?(string)
!string.strip.include? " "
end
But if you want to allow a particular set of characters that meet your definition of word, perhaps including apostrophes and hyphens, use a regex:
def single_word?(string)
string.scan(/[\w'-]+/).length == 1
end
Following your definition of a word given in the comment:
[A] stripped string that doesn't [include] whitespace
the code would be
def check_if_single_word(string)
string.strip == string and string.include?(" ").!
end
check_if_single_word("two words") # => false
check_if_single_word("New York") # => false
check_if_single_word("hello") # => true
check_if_single_word(" hello") # => false
Here some code may help you out :
def check_if_single_word(string)
ar = string.scan(/\w+/)
ar.size == 1 ? "only one word" : "more than one word"
end
s1 = "two words"
s2 = "hello"
check_if_single_word s1 # => "more than one word"
check_if_single_word s2 # => "only one word"
def check_if_single_word(string)
string.scan(/\w+/).size == 1
end
s1 = "two words"
s2 = "hello"
check_if_single_word s1 # => false
check_if_single_word s2 # => true
I would check if a space exists in the string.
def check_if_single_word(string)
return !(string.strip =~ / /)
end
.strip will remove excess white space that may exist at the start and the end of the string.
!(myString =~ / /) means that the string does not match the regular expression of a single space.
Likewise you could also use !string.strip[/ /].
a Ruby Way. Extend the calss String
class String
def one?
!self.strip.include? " "
end
end
Then use "Hello world".one? to Check if string contains one word or more.
I have a long string, "the silver rider on his back and the palm tree". I would like to write a Ruby method that capitalizes all words except "on", "the", and "and" in the middle of the sentence, but have the "the" capitalized at the beginning?
Here is what I have so far:
def title(word)
small_words = %w[on the and]
word.split(' ').map do |w|
unless small_words.include?(w)
w.capitalize
else
w
end
end.join(' ')
end
This code actually does most of what I need but don't know how to include or exclude for that matter the "the" at the beginning of the sentence.
This will capitalize all the words, except for the stop words (your small words) that aren't the first in the sentence.
def title(sentence)
stop_words = %w{a an and the or for of nor} #there is no such thing as a definite list of stop words, so you may edit it according to your needs.
sentence.split.each_with_index.map{|word, index| stop_words.include?(word) && index > 0 ? word : word.capitalize }.join(" ")
end
It’s easiest to just forget about the special case of the first letter initially and then handle it after doing everything else:
def title(sentence)
small_words = %w[on the and]
capitalized_words = sentence.split(' ').map do |word|
small_words.include?(word) ? word : word.capitalize
end
capitalized_words.first.capitalize!
capitalized_words.join(' ')
end
This also capitalizes any “small word” at the beginning, not just “the”—but I think that’s probably what you want anyway.
A simple mod to your existing code would make it work:
def title( word )
small_words = %w[on the and]
word.split(' ').map.with_index do |w, i|
unless (small_words.include? w) and (i > 0)
w.capitalize
else
w
end
end.join(' ')
end
SmallWords = %w[on the and]
def title word
word.gsub(/[\w']+/){
SmallWords.include?($&) && $~.begin(0).zero?.! ? $& : $&.capitalize
}
end
I think I'm close, but the regex isn't evaluating. Hoping someone may know why.
def new_title(title)
words = title.split(' ')
words = [words[0].capitalize] + words[1..-1].map do |w|
if w =~ /and|an|a|the|in|if|of/
w
else
w.capitalize
end
end
words.join(' ')
end
When I pass in lowercase titles, they get returned as lowercase.
You need to properly anchor your regular expression:
new_title("the last hope")
# => "The last Hope"
This is because /a/ matches a word with an a in it. /\Aa\Z/ matches a string that consists entirely of a, and /\A(a|of|...)\Z/ matches against a set of words.
In any case, what you might want is this:
case (w)
when 'and', 'an', 'a', 'the', 'in', 'if', 'of'
w
else
w.capitalize
end
Using a regular expression here is a bit heavy handed. What you want is an exclusion list.
This is called titleize, and is implemented like this:
def titleize(word)
humanize(underscore(word)).gsub(/\b('?[a-z])/) { $1.capitalize }
end
Se the doc.
If you want fancy titlezing, check out granth's titleize
Your regular expression should be checking the whole word (^word$). Anyway, isn't more simple to use Enumerable#include?:
def new_title(title)
words = title.split(' ')
rest_words = words.drop(1).map do |word|
%w(and an a the in if of).include?(word) ? word : word.capitalize
end
([words[0].capitalize] + rest_words).join(" ")
end