Ruby printf"%-*.*s" problems - ruby

I am trying to use this line of code here:
longest = twoOfArray.inject(0) {|memo,word| memo > word.length ? memo : word.length},
and put it into this printf on this line like this:
twoOfArray.each {|k, v| puts "%-*.*s" % [longest] + " " + '*'*v }
However, it gives me the error:
%': too few arguments (ArgumentError)
Does this mean that something is wrong with my longest variable? Or is my syntax wrong? I cant seem to fix the problem. Does anyone see whats wrong here?

The format string "%-*.*s" expects three arguments: two numbers (one for each*`) and a string. Your array only contains a single argument.

hash = {"it"=>3, "was"=>3, "the"=>3, "of"=>2, "times"=>2}
words_array = hash.keys
longest_word = words_array.max_by {|word| word.length}
max_len = longest_word.size
words_array.each do |word|
number = hash[word]
str = "#{'*' * (max_len - number)}#{word}#{'*' * number}"
puts str
end
--output:--
**it***
**was***
**the***
***of**
***times**

Related

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

Converting even indexed characters in a string to uppercase ruby

I need to convert all the even indexed characters in a string to become uppercase, while the odd indexed characters stay lowercase. I've tried this, but it keeps failing and I'm not sure why. I'd appreciate some help!
for i in 0..string.length
if (i % 2) == 0
string[i].upcase
else
string[i].downcase
end
end
"foobar".gsub(/(.)(.?)/){$1.upcase + $2.downcase} # => "FoObAr"
"fooba".gsub(/(.)(.?)/){$1.upcase + $2.downcase} # => "FoObA"
There you go:
string = "asfewfgv"
(0...string.size).each do |i|
string[i] = i.even? ? string[i].upcase : string[i].downcase
end
string # => "AsFeWfGv"
We people don't use for loop usually, that's why I gave the above code. But here is correct version of yours :
string = "asfewfgv"
for i in 0...string.length # here ... instead of ..
string[i] = if (i % 2) == 0
string[i].upcase
else
string[i].downcase
end
end
string # => "AsFeWfGv"
You were doing it correctly, you just forgot to reassign it the string index after upcasing or downcasing.
You have two problems with your code:
for i in 0..string.length should be for i in 0...string.length to make the last character evaluated string[string.length-1], rather than going past the end of the string (string[string.length]); and
string[i] must be an L-value; that is, you must have, for example, string[i] = string[i].upcase.
You can correct your code and make it more idiomatic as follows:
string = "The cat in the hat"
string.length.times do |i|
string[i] = i.even? ? string[i].upcase : string[i].downcase
end
string
#=> "ThE CaT In tHe hAt"

How do I join the final result so it is not an array, but a string?

I've written a method for adding ay to the ends of words if they begin with a vowel. If the words begin with a consonant it will move the consonants to the end of the word and then add ay.
My issue with this is that my result is returned in an array for example:
translate("happy animals")
Instead of getting "appyhay animalsay"
I get ["appyhay", "animalsay"]
I tried joining them at the end, but when I run the test it says that the join method could not be found?
Is this just a mess or am I getting close?
Many thanks for any insight :)
def translate(word)
multiplewords = word.split(" ")
multiplewords.map! do |x|
separated = x.split("")
if !'aeiou'.include?(separated[0])
while !'aeiou'.include?(separated[0])
letter = separated.shift
separated << letter
separated
end
final = separated.join("") + "ay"
else
final = separated.join("") + "ay"
end
end
end
translate("happy animals") => ['appyhay', 'animlasay']
Answer needed: "appyhay animalsay"
You should join it at the last part. I tried to simplify it a bit as well.
#!/usr/bin/env ruby
def translate(word)
word.split(" ").map do |x|
separated = x.split("")
if !'aeiou'.include?(separated[0])
while !'aeiou'.include?(separated[0])
letter = separated.shift
separated << letter
end
end
separated.join("") + "ay"
end.join(' ')
end
puts translate("happy animals")
Output:
appyhay animalsay

Ruby string concatenation add space

I need to make a method that repeats a given word but I think I am designing it wrong. I need spaces between the words, what am I missing here?
def repeat(word, repeats=2)
sentence = word.to_s * repeats
return sentence
end
Of course, you are missing spaces.
You could have done it like this:
def repeat(word, repeats = 2)
Array.new(repeats, word).join(" ")
end
You can write the code as below :
def repeat(word, repeats=2)
([word] * repeats).join(" ")
end
repeat("Hello",4)
# => "Hello Hello Hello Hello"
Here's one closer to your approach and without using a temporary array:
def repeat(word, repeats=2)
("#{word} " * repeats).chop
end
"#{word} " converts word to a string using string interpolation and appends a space
… * repeats creates a string containing repeats copies
.chop returns the string with the last character removed (the trailing space)
Not having to create an array makes the code a bit faster.
w = 'kokot'
n = 13
n.times.map { w.each_char.to_a.shuffle.join }.tap { |a, _| a.capitalize! }.join(' ') << ?.

Ruby: Conditionally change the last element in array

I have an array s_ary that contains all the lines of a text document. The last line may be something like return some_string.
When the last line starts with return, I want the line to become sv1 = some_string.
My original code does exactly what I need:
if s_ary.last =~ /^[\s]*return/
t = s_ary.last.gsub(/^[\s]*return/, 'sv1 = ')
s_ary.pop
s_ary << t
else
s_ary << 'sv1 = last'
end
I tried to improve it with:
if s_ary.last =~ /^[\s]*return/
s_ary.map! {|x| (x =~/return/) ? x.gsub(/return/, 'sv1 = ') : x }
else
s_ary << 'sv1 = last'
end
But this version will change all the lines that start with return when the last line starts with return. I could still use this last version as this is not supposed to happen in my application, but I have the feeling that there must a better, more compact way of accomplishing this. Somehow, I can't find it.
Thanks for any suggestion.
EDIT: The original code that does exactly what I need is actually (in agreement with the second paragraph of this post):
if s_ary.last =~ /^[\s]*return/
t = s_ary.last.gsub(/^[\s]*return/, 'sv1 = ')
s_ary.pop
s_ary << t
end
I can't believe I wrote something so confusing. My apologies.
Why not just:
s_ary[-1].gsub!(/^return /, 'sv1 = ')
EDIT
Looking closer at your code, maybe what you want is kind of:
unless s_ary[-1].gsub!(/^return /, 'sv1 = ')
s_ary << 'sv1 = last'
end
The first snippet will only change the last line if it starts with return, doing nothing otherwise. The second snippet will change the last line if it starts with return, and will append sv1 = last otherwise.
Not sure which one you need.
arr = ["Foo is good","Bar is bad","return v1 + v2"]
arr[-1] = arr.last.sub("return","sv = ") if arr.last.start_with? "return"
p arr #=> "Foo is good", "Bar is bad", "sv = v1 + v2"]

Resources