How to ignore nonword charcters in Ruby? - ruby

I need to create a method called palindrome?
The palindrome?(string) must determine whether a given string (word or phrase) is a palindrome, that is, it reads the same backwards as forwards, ignoring case, punctuation, and nonword characters.
I can't understand what method to use to ignore nonword characters
def palindrome?(str)
if str.reverse.downcase.delete(" ") == str.downcase.delete(" ")
return true
else
return false
end
end
I am using the ruby language and I can use Regex's, also to note that I feel that my code is too complex I know I can make it much simpler please help

def palindrome? str
str = str.gsub(/\W/, "").downcase
str.reverse == str
end

Related

Insert a space in string

I am trying to .insert a space before the Uppercase letter if it's found.
Here's what I came up with, but it seems like it's an infinite loop. I don't know why:
def solution(string)
str = string.split("")
str.each_with_index do |l, i|
if l.upcase
str.insert(l[i], " ")
end
end
str.join("")
end
please let me know what I'm missing.
Because it's often a bad idea changing the object you're looping on. You insert a space before the upcase letter you found, so the next iteration you found the upcase letter again and everything repeats.
In this case regular expression seems to fit nicely
def solution(string)
string.gsub(/[[:upper:]]/, ' \0')
end

How can I create a method that checks if a string starts with a capitalized letter?

So far I have:
def capitalized?(str)
str[0] == str[0].upcase
end
THe problem wit this is that it returns true for strings like "12345", "£$%^&" and"9ball" etc. I would like it to only return true if the first character is a capital letter.
You can use match? to return true if the first character is a letter in the range of A to Z both uppercase or not:
def capitalized?(str)
str.match?(/\A[A-Z]/)
end
p capitalized?("12345") # false
p capitalized?("fooo") # false
p capitalized?("Fooo") # true
Also you can pass a regular expression to start_with?:
p 'Foo'.start_with?(/[A-Z]/) # true
p 'foo'.start_with?(/[A-Z]/) # false
There's probably a nicer way to do it with regex, but keeping this ruby based, you can make an array of capital letters:
capital_letters = ("A".."Z")
Then you can check if your first letter is in that array:
def capitalized?(str)
capital_letters = ("A".."Z")
capital_letters.include?(str[0])
end
Or a bit shorter:
def capitalized?(str)
("A".."Z").include?(str[0])
end
I would avoid character ranges if possible, because without knowing the encoding, you can never be sure what is in a range. In your case, it is unnecessary. A simple
/^[[:upper:]]/ =~ str
would do. See here for the definition of POSIX character classes.
def capitalized?(str)
str[0] != str[0].downcase
end
capitalized? "Hello" #=> true
capitalized? "hello" #=> false
capitalized? "007, I presume" #=> false
capitalized? "$100 for that?" #=> false
Simple solution
def capitalized?(str)
str == str.capitalize
end

Taking a string and returning it with vowels removed

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

"If equal to a letter"

I am trying to write a method that takes in a string that will have no spaces. All I want it to do is to return "bug" when the character in the string is a letter [A-Za-z] and to return "ant" if it's any other character.
def Letter(str)
(0..str.length).to_a do |index|
if str[index].chr =~ /[A-Za-z]/ ##I think this is where things are going wrong.
puts "bug"
else
puts "ant"
end
end
end
Does anyone have any idea how to fix this? I keep getting arrays of consecutive numbers.
Rewritten
def letter(str)
str.each_char.map do |char|
(char =~ /[[:alpha:]]/) ? 'bug' : 'ant'
end
end
In your code, you are trying to print "bug" or "ant"; but you're returning (0..str.length).to_a. This function will return an array of bugs and ants. It is also made more Rubyish:
methods should be in snake_case (lowercase, with underscores between words)
iterating over strings is easier with each_char
it's fine with [A-Za-z], but [[:alpha:]] is both clearer and handles Unicode stuff.
since we're testing each character, you know it's going to be one character long, so you don't need the start of line and end of line anchors.
def letter(str)
str.chars.each do |x|
puts x=~ /[A-Za-z]/ ? "bug" : "ant"
end
end
First things first, in your loop your are trying to convert a range
into an array.
(0..str.length).to_a
str.length returns a number, therefore making it into an array will give you an array of numbers. Hence your problem.
Second, you have to have brackets around your /a-zA-Z/ regex
Third, use the ternary operator. It's great for small if statements. Heres the syntax:
boolean ? "if boolean is true this code will execute" : "else this code will"
Fourth, use the .each methods, ruby is loved partly because of the simplicity of loops and iterating!
Happy coding!

How do I obtain the (possibly nested) capture groups in a regular expression?

Given a regular expression:
/say (hullo|goodbye) to my lovely (.*)/
and a string:
"my $2 is happy that you said $1"
What is the best way to obtain a regular expression from the string that contains the capture groups in the regular expression? That is:
/my (.*) is happy that you said (hullo|goodbye)/
Clearly I could use regular expressions on a string representation of the original regular expression, but this would probably present difficulties with nested capture groups.
I'm using Ruby. My simple implementation so far goes along the lines of:
class Regexp
def capture_groups
self.to_s[1..-2].scan(/\(.*?\)/)
end
end
regexp.capture_groups.each_with_index do |capture, idx|
string.gsub!("$#{idx+1}", capture)
end
/^#{string}$/
i guess you need to create your own function that would do this:
create empty dictionaries groups and active_groups and initialize counter = 1
iterate over the characters in the string representation:
if current character = '(' and previous charaster != \:
add counter key to active_groups and increase counter
add current character to all active_groups
if current character = ')' and previous charaster != \:
remove the last item (key, value) from active_groups and add it to groups
convert groups to an array if needed
You might also want to implement:
ignore = True between unescaped '[' and ']'
reset counter if current character = '|' and active_groups is empty (or decrease counter if active_group is not empty)
UPDATES from comments:
ingore non-capturing groups starting with '(?:'
So once I realised that what I actually need is a regular expression parser, things started falling into place. I discovered this project:
https://github.com/dche/randall
which can generate strings that match a regular expression. It defines a regular expression grammar using http://treetop.rubyforge.org/. Unfortunately the grammar it defines is incomplete, though useful for many cases.
I also stumbled past https://github.com/mjijackson/citrus, which does a similar job to Treetop.
I then found this mind blowing gem:
https://github.com/ammar/regexp_parser
which defines a full regexp grammar and parses a regular expression into a walkable tree. I was then able to walk the tree and pick out the parts of the tree I wanted (the capture groups).
Unfortunately there was a minor bug, fixed in my fork: https://github.com/LaunchThing/regexp_parser.
Here's my patch to Regexp, that uses the fixed gem:
class Regexp
def parse
Regexp::Parser.parse(self.to_s, 'ruby/1.9')
end
def walk(e = self.parse, depth = 0, &block)
block.call(e, depth)
unless e.expressions.empty?
e.each do |s|
walk(s, depth+1, &block)
end
end
end
def capture_groups
capture_groups = []
walk do |e, depth|
capture_groups << e.to_s if Regexp::Expression::Group::Capture === e
end
capture_groups
end
end
I can then use this in my application to make replacements in my string - the final goal - along these lines:
from = /^\/search\/(.*)$/
to = '/buy/$1'
to_as_regexp = to.dup
# I should probably make this gsub tighter
from.capture_groups.each_with_index do |capture, idx|
to_as_regexp.gsub!("$#{idx+1}", capture)
end
to_as_regexp = /^#{to_as_regexp}$/
# to_as_regexp = /^\/buy\/(.*)$/
I hope this helps someone else out.

Resources