I want to replace a character in string when a particular condition is satisfied. So , I went through the API doc of Ruby and found the gsub , gsub! etc for similar purpose. When I implemented that in my program I didn't got any error but din't got the desired output too.
The code which I was trying is this :
name.each_char { |c|
if name[c] == "a"
name.sub( name[c] , c )
puts "matched.... "
end
So , for example , I have a string called huzefa and want to replace all the letters with its index numbers . So , what is the way to do it ? Please explain in detail by giving a simple example.
You could pass block to gsub and do whatever you want when match happend.
To do it inplace you could use gsub! method.
name = "Amanda"
new_name = name.gsub("a") do |letter|
puts "I've met letter: " + letter
"*"
end
# I've met letter: a
# I've met letter: a
# => "Am*nd*"
If you want to work with indexes, you could do something like this:
new_name = name.chars.map.with_index do |c, i|
if i.odd?
"*"
else
c
end
end.join
#=> => "A*a*d*"
Here c and i are passed to the block. c is a character and i is an index.
if name=huzefa and you want to replace 'a' with its index..
name.split(//).map.with_index{|x,y| (x=='a')? y : x}.join
to result in #> "huzef5"
Related
My goal is to have the user enter a string to find a string in an array. Im using strings include? function to search but its returning the wrong data.
puts "Enter Artist(all or partial name):"
search_artist = gets.chomp
list.each do |x|
if x.artist.include? (search_artist)
num += 1
x.to_s
else
puts "none found"
end end
search_artist = 'a' (because im looking for AARON...)
returns:
AARON KDL NOT VALID 2
ZAC CHICKEN ROCK 1289
2 records found
should be:
AARON KDL NOT VALID 2
1 record found`
The problem is that both strings include 'a' somewhere in the string.
How do I search from the beginning of the string?
There's a really easy way of doing this with grep:
matches = list.grep(search_artist)
if (matches.empty?)
puts "none found"
end
To count the number of matches you can just matches.length.
If you want a case insensitive match, then you want this:
matches = list.grep(Regexp.new(search_artist, Regexp::IGNORECASE))
Where that flag creates a case-insensitive regular expression to match more broadly.
Edit: To anchor this search to the beginning of the string:
matches = list.grep(Regexp.new('\A' + Regexp.escape(search_artist), Regexp::IGNORECASE))
Where \A anchors to the beginning of the string.
Other option, just if the search is limited to the first letter, case insensitive:
found = list.select { |x| [search_artist.downcase, search_artist.upcase].include? x[0] }
found.each { |e| puts e }
puts "Found #{found.size} records"
Without Regular expressions:
puts "Enter Artist(all or partial name):"
search_artist = gets.chomp
puts list.select do |x|
x.artist.start_with?(search_artist)
end
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
I answered my own question. Forgot to initialize count = 0
I have a bunch of sentences in a paragraph.
a = "Hello there. this is the best class. but does not offer anything." as an example.
To figure out if the first letter is capitalized, my thought is to .split the string so that a_sentence = a.split(".")
I know I can "hello world".capitalize! so that if it was nil it means to me that it was already capitalized
EDIT
Now I can use array method to go through value and use '.capitalize!
And I know I can check if something is .strip.capitalize!.nil?
But I can't seem to output how many were capitalized.
EDIT
a_sentence.each do |sentence|
if (sentence.strip.capitalize!.nil?)
count += 1
puts "#{count} capitalized"
end
end
It outputs:
1 capitalized
Thanks for all your help. I'll stick with the above code I can understand within the framework I only know in Ruby. :)
Try this:
b = []
a.split(".").each do |sentence|
b << sentence.strip.capitalize
end
b = b.join(". ") + "."
# => "Hello there. This is the best class. But does not offer anything."
Your post's title is misleading because from your code, it seems that you want to get the count of capitalized letters at the beginning of a sentence.
Assuming that every sentence is finishing on a period (a full stop) followed by a space, the following should work for you:
split_str = ". "
regex = /^[A-Z]/
paragraph_text.split(split_str).count do |sentence|
regex.match(sentence)
end
And if you want to simply ensure that each starting letter is capitalized, you could try the following:
paragraph_text.split(split_str).map(&:capitalize).join(split_str) + split_str
There's no need to split the string into sentences:
str = "It was the best of times. sound familiar? Out, damn spot! oh, my."
str.scan(/(?:^|[.!?]\s)\s*\K[A-Z]/).length
#=> 2
The regex could be written with documentation by adding x after the closing /:
r = /
(?: # start a non-capture group
^|[.!?]\s # match ^ or (|) any of ([]) ., ! or ?, then one whitespace char
) # end non-capture group
\s* # match any number of whitespace chars
\K # forget the preceding match
[A-Z] # match one capital letter
/x
a = str.scan(r)
#=> ["I", "O"]
a.length
#=> 2
Instead of Array#length, you could use its alias, size, or Array#count.
You can count how many were capitalized, like this:
a = "Hello there. this is the best class. but does not offer anything."
a_sentence = a.split(".")
a_sentence.inject(0) { |sum, s| s.strip!; s.capitalize!.nil? ? sum += 1 : sum }
# => 1
a_sentence
# => ["Hello there", "This is the best class", "But does not offer anything"]
And then put it back together, like this:
"#{a_sentence.join('. ')}."
# => "Hello there. This is the best class. But does not offer anything."
EDIT
As #Humza sugested, you could use count:
a_sentence.count { |s| s.strip!; s.capitalize!.nil? }
# => 1
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
I'm playing around with Ruby to do some file versioning for me. I have a string 2.0.0.65 . I split it up, increment the build number (65 --> 66) then I want to replace the 65 with the 66. In this replace though, I only want to replace the last match of the string. What's the best way in Ruby to do this?
version_text = IO.read('C:\\Properties')
puts version_text
version = version_text.match(/(\d+\.\d+\.\d+\.\d+)/)[1]
puts version
build_version = version.split('.')[3]
puts build_version
incremented_version = build_version.to_i + 1
puts incremented_version`
...
If you just want to increment the integer at the very end of a string then try this:
s = '2.0.0.65'
s.sub(/\d+\Z/) {|x| x.to_i + 1} # => '2.0.0.66'
You can do something like this:
parts = "2.0.0.65".split('.')
parts[3] = parts[3].to_i + 1
puts parts.join(".")
output:
2.0.0.66
This gives you more control over just using a string replacement method, as now you can increment other parts of the version string if needed more easily.
Once you have the string with the build number, you only need to use 'succ' method
'2.0.0.65'.succ()
Which gives you the string
'2.0.0.66'
sample = '2.0.0.65'
def incr_version(version)
parts = version.split('.')
parts[-1] = parts[-1].to_i + 1
parts.join('.')
end
incr_version(sample) # => '2.0.0.66'
For fun, if you want to increment the last integer in any string you could do this:
str = "I have 3 cats and 41 rabbits"
str.reverse.sub(/\d+/){ |s| (s.reverse.to_i+1).to_s.reverse }.reverse
#=> "I have 3 cats and 42 rabbits"
This is only valid when you modify your regex to match the reversed version of the text.
More generally, you can do this:
class String
# Replace the last occurrence of a regex in a string.
# As with `sub` you may specify matches in the replacement string,
# or pass a block instead of the replacement string.
# Unlike `sub` the captured sub-expressions will be passed as
# additional parameters to your block.
def rsub!(pattern,replacement=nil)
if n=rindex(pattern)
found=match(pattern,n)
self[n,found[0].length] = if replacement
replacement.gsub(/\\\d+/){ |s| found[s[1..-1].to_i] || s }
else
yield(*found).to_s
end
end
end
def rsub(pattern,replacement=nil,&block)
dup.tap{ |s| s.rsub!(pattern,replacement,&block) }
end
end
str = "I have 3 cats and 41 rabbits"
puts str.rsub(/(?<=\D)(\d+)/,'xx')
#=> I have 3 cats and xx rabbits
puts str.rsub(/(?<=\D)(\d+)/,'-\1-')
#=> I have 3 cats and -41- rabbits
puts str.rsub(/(?<=\D)(\d+)/){ |n| n.to_i+1 }
#=> I have 3 cats and 42 rabbits
Note that (as with rindex) because the regex search starts from the end of the string you may need to make a slightly more complex regex to force your match to be greedy.