Placing a box around centered text in Ruby - ruby

This is a super basic ruby question. Just learning ruby and I'm trying to build out a box where it finds the longest word in the string and makes the box that width with spaces on either side and then centers all the other text i've passed in.
so far i have this;
def box(str)
arr = str.split
wordlength = arr.max_by(&:length).length
width = wordlength + 4
width.times{print "*"}
puts "\n"
arr.each {|i| puts "* #{i} *" }
width.times{print "*"}
end
but the above prints out:
***********
* texting *
* stuff *
* test *
* text *
***********
i'd like it to print something like the below
***********
* texting *
* stuff *
* test *
* text *
***********
thanks

Here, this code works:
def box(str)
arr = str.split
wordlength = arr.max_by(&:length).length
width = wordlength + 4
width.times{print "*"}
puts "\n"
arr.each do |i|
current_length = i.length
puts "* #{fill_space(current_length, wordlength, 'pre', i)}#{i}#{fill_space(current_length, wordlength, 'post')} *"
end
width.times{print "*"}
end
def fill_space(current_length, max_length, where, current='')
spaces_to_fill = max_length - current_length
if where == 'pre'
str = ' ' * (spaces_to_fill / 2)
elsif spaces_to_fill % 2 > 0
str = ' ' * (spaces_to_fill / 2 + 1)
else
str = ' ' * (spaces_to_fill / 2)
end
end
The problem was,that you didn't calculate how many " " you should insert into your current row. In the fill_space function I calculate exactly that, and I call this function for each row that should be printed. Also, if there are odd words this function adds the additional space in the end of the row.
I didn't change much of your box function but feel free to insert the hint that Keith gave you

Related

Ruby overloading operators with severals parameters

I have a class MoneyBox with two fields (wallet and cent). I need to overload operations +, -, and * with these fields. How to correctly implement the overloading of these operators with two parameters?
class MoneyBox
attr_accessor :wallet, :cent
def initialize(wallet, cent)
#wallet = wallet
#cent = cent
end
def + (obj, obj_cent)
self.wallet = self.wallet + obj.wallet
self.cent = self.cent + obj_cent.cent
return self.wallet, self.cent
end
def - (obj, obj_cent)
self.wallet = self.wallet - obj.wallet
self.cent = self.cent - obj_cent.cent
return self.wallet, self.cent
end
def * (obj,obj_cent)
self.wallet = self.wallet * obj.wallet
self.cent = self.cent * obj_cent
return self.wallet, self.cent
end
end
It should be something like this:
Cash1 = MoneyBox.new(500, 30)
Cash2 = MoneyBox.new(100, 15)
puts " D1 = #{D1 = (Cash1-(Cash2*2))}" # 500,30 - 100,15*2 = 300,0
You already have a class that encapsulates wallet / cent pairs and the operations are also performed pair-wise. Why not take advantage of it and make +, - and * take a (single) MoneyBox instance as their argument and return the result as another MoneyBox instance, e.g.: (arithmetic operators shouldn't modify their operands)
class MoneyBox
attr_accessor :wallet, :cent
def initialize(wallet, cent)
#wallet = wallet
#cent = cent
end
def +(other)
MoneyBox.new(wallet + other.wallet, cent + other.cent)
end
def -(other)
MoneyBox.new(wallet - other.wallet, cent - other.cent)
end
def *(other)
MoneyBox.new(wallet * other.wallet, cent * other.cent)
end
def to_s
"#{wallet},#{cent}"
end
end
Example usage:
cash1 = MoneyBox.new(500, 30)
cash2 = MoneyBox.new(100, 15)
puts "#{cash1} + #{cash2} = #{cash1 + cash2}"
# 500,30 + 100,15 = 600,45
puts "#{cash1} - #{cash2} = #{cash1 - cash2}"
# 500,30 - 100,15 = 400,15
puts "#{cash1} * #{cash2} = #{cash1 * cash2}"
# 500,30 * 100,15 = 50000,45
To multiply both wallet and cents by 2 you'd use a MoneyBox.new(2, 2) instance:
puts "#{cash1} - #{cash2} * 2 = #{cash1 - cash2 * MoneyBox.new(2, 2)}"
# 500,30 - 100,15 * 2 = 300,0
Note that operator precedence still applies, so the result is evaluated as cash1 - (cash2 * Money.new(2, 2)).
If you want to multiply by integers directly, i.e. without explicitly creating that MoneyBox.new(2, 2) instance, you could move that logic into * by adding a conditional, e.g:
def *(other)
case other
when Integer
MoneyBox.new(wallet * other, cent * other)
when MoneyBox
MoneyBox.new(wallet * other.wallet, cent * other.cent)
else
raise ArgumentError, "expected Integer or MoneyBox, got #{other.class}"
end
end
Which gives you:
cash1 = MoneyBox.new(500, 30)
cash2 = MoneyBox.new(100, 15)
puts "#{cash1} - #{cash2} * 2 = #{cash1 - cash2 * 2}"
# 500,30 - 100,15 * 2 = 300,0
Note that this only defines MoneyBox#* and doesn't alter Integer#*, so 2 * cash2 does not work by default:
2 * cash2
# TypeError: MoneyBox can't be coerced into Integer
That's because you're calling * on 2 and Integer doesn't know how to deal with a MoneyBox instance. This could be fixed by implementing coerce in MoneyBox: (something that only works for numerics)
def coerce(other)
[self, other]
end
Which effectively turns 2 * cash2 into cash2 * 2.
BTW if you always call * with an integer and there's no need to actually multiply two MoneyBox instances, it would of course get much simpler:
def *(factor)
MoneyBox.new(wallet * factor, cent * factor)
end
You can't use the typical x + y syntax if you're going to overload the operator to take two arguments.
Instead, you have to use .+, see the following:
class Foo
def initialize(val)
#val = val
end
def +(a,b)
#val + a + b
end
end
foo = Foo.new(1)
foo.+(2,3)
# => 6
Your example usage does not match the method definition. You want to use your objects in the way to write expressions like i.e. Cash2 * 2). Since Cash2 is a constant of class MoneyBox, the definition for the multiplication by a scalar would be something like
def * (factor)
self.class.new(wallet, cent * factor)
end
This is a shortcut of
def * (factor)
MoneyBox.new(self.wallet, self.cent * factor)
end

How to delete double quotation mark from ruby array?

Based on the link
I tried to delete "" in the array on ruby
However still not get what I want, if anyone knows, please advice me
a = gets
lines = []
aaa = []
b = []
bb =[]
while line = gets do
lines << line.chomp.split(' ')
end
for k in 0..(lines.size - 1) do
b << lines[k][1].to_i + 1
end
for i in 0..(lines.size - 1)do
bb << lines[i][0] + ' ' + b[i].to_s
end
for l in 0..(lines.size - 1)do
p bb[l]
end
Input
3
Tanaka 18
Sato 50
Suzuki 120
Output
[["Tanaka", "18"], ["Sato", "50"], ["Suzuki", "120"]]
"Tanaka 19"
"Tanaka 19"
"Sato 51"
"Suzuki 121"
As pointed out in the comments, you can get rid of the quotation marks by replacing p (Ruby's inspect/print) with puts.
While we're at it, you can make this much more "Ruby-ish" by using .readlines to scoop up all the input into an array, and by replacing the multiple counting loops with .map or .each iterators. The following is more concise, and allows you to lose the first input line which you're just throwing away anyway.
lines = STDIN.readlines(chomp: true).map do |line|
l = line.split(' ')
[l[0], l[1].to_i + 1].join(' ')
# or
# "#{l[0]} #{l[1].to_i + 1}"
end
lines.each { |line| puts line }
With Ruby 3, you can use rightward-assignment for the first part if you find it more readable:
STDIN.readlines(chomp: true).map do |line|
l = line.split(' ')
"#{l[0]} #{l[1].to_i + 1}"
end => lines

Print frame around user input words or sentences

I am doing a coding bootcamp and into week-2 and have learnt loop, methods, a bit of classes. I have come across with this coding challenge.
I can put a frame around the sentence I put in def method. I would like to know how I can put a frame around user input words. I have tried, but am getting an error.
Ultimately, what I want to achieve is to delete words = %w(This restaurant has an excellent menu - sushi ramen okonomiyaki.) from def method.
Thank you!!
puts "Welcome to frame with words."
puts "Enter your favourite quote or sentence or any word you like"
words = gets.chomp.to_s
def my_favourite words=[]
words = %w(This restaurant has an excellent menu - sushi ramen okonomiyaki.)
longest = 0
words.each {|word| longest = word.length if longest < word.length }
(0..longest+3).each {print "*"}
print "\n"
words.each do |word|
print "* "
print word
(0..longest-word.length).each { print " " }
print"*\n"
end
(0..longest+3).each {print"*" }
return
end
my_favourite
The type of output I want is below.
* This *
* restaurant *
* has *
* an *
* excellent *
* menu *
* - *
* sushi *
* ramen *
* okonomiyaki. *
**************** ```
Suppose
str = gets.chomp
#=> "This restaurant has an excellent menu - sushi ramen."
then
words = str.split
#=> ["This", "restaurant", "has", "an", "excellent", "menu", "-",
# "sushi", "ramen."]
width = words.max_by(&:size).size
#=> 10
top_bot = '*' * (width+4)
#=> "**************"
puts top_bot
words.each { |word| puts "* %-#{width}s *" % word }
puts top_bot
displays:
**************
* This *
* restaurant *
* has *
* an *
* excellent *
* menu *
* - *
* sushi *
* ramen. *
**************
words.max_by(&:size) is shorthand for:
words.max_by { |word| word.size }
#=> "restaurant"
See the doc Kernel#sprint for an explanation of the formatting codes in "* %-#{width}s *" % word. After #{width} is substitute out this becomes "* %-10s *" % word. s specifies that word is a string. 10 means it should occupy a field of width 10. - means that the string should be left-adjusted in the field. The field of width 10 is preceded by "* " and followed by " *", forming a string of width 14.
This could instead be (equivalently) written:
word = "balloon"
sprintf("* %-#{width}s *", word)
#=> "* balloon *"
See also String#split, Enumerable#max_by and String#*.

trouble appending hash value ruby

I'm writing a program which takes input, stores it as a hash and sorts the values.
I'm having trouble comparing a current hash value with a variable.
Sample Input:
3
A 1
B 3
C 5
A 2
B 7
C 2
Sample Output:
A 1 2
B 3 7
C 2 5
Everything works apart from this part, and I'm unsure why.
if values.key?(:keys)
if values[keys] >= val
values.store(keys,val.prepend(val + " "))
else
values.store(keys,val.concat(" " + val))
end
else
values.store(keys,val)
end
i = i + 1
end
Rest of code:
#get amount of records
size = gets.chomp
puts size
size = size.to_i
values = Hash.new(0)
i = 0
while i < (size * 2)
text = gets.chomp
#split string and remove space
keys = text.split[0]
val = text.split[1]
#check if key already exists,
# if current value is greater than new value append new value to end
# else put at beginning of current value
if values.key?(:keys)
if values[keys] >= val
values.store(keys,val.prepend(val + " "))
else
values.store(keys,val.concat(" " + val))
end
else
values.store(keys,val)
end
i = i + 1
end
#sort hash by key
values = values.sort_by { |key, value| key}
#output hash values
values.each{|key, value|
puts "#{key}:#{value}"
}
Could anyone help me out? It would be most appreciated.
The short answer is that there are two mistakes in your code. Here is the fixed version:
if values.key?(keys)
if values[keys] >= val
values.store(keys,values[keys].prepend(val + " "))
else
values.store(keys,values[keys].concat(" " + val))
end
else
values.store(keys,val)
end
The if statement was always evaluating as false, because you were looking for hash key named :keys (which is a Symbol), not the variable you've declared named keys.
Even with that fixed, there was a second hidden bug: You were storing a incorrect new hash value. val.concat(" " + val) would give you results like A 2 2, not A 1 2, since it's using the new value twice, not the original value.
With that said, you code is still very confusing to read... Your variables are size, i, text, val, values, key and keys. It would have been a lot easier to understand with clearer variable names, if nothing else :)
Here is a slightly improved version, without changing the overall structure of your code:
puts "How may variables to loop through?"
result_length = gets.chomp.to_i
result = {}
puts "Enter #{result_length * 2} key-value pairs:"
(result_length * 2).times do
input = gets.chomp
input_key = input.split[0]
input_value = input.split[1]
#check if key already exists,
# if current value is greater than new value append new value to end
# else put at beginning of current value
if result.key?(input_key)
if result[input_key] >= input_value
result[input_key] = "#{input_value} #{result[input_key]}"
else
result[input_key] = "#{result[input_key]} #{input_value}"
end
else
result[input_key] = input_value
end
end
#sort hash by key
result.sort.to_h
#output hash result
result.each{|key, value|
puts "#{key}:#{value}"
}
h = Hash.new { |h,k| h[k] = [] }
input = ['A 1', 'B 3', 'C 5', 'A 2', 'B 7', 'C 2'].join("\n")
input.each_line { |x| h[$1] << $2 if x =~ /^(.*?)\s+(.*?)$/ }
h.keys.sort.each do |k|
puts ([k] + h[k].sort).join(' ')
end
# A 1 2
# B 3 7
# C 2 5
This would be a more Ruby-ish way to write your code :
input = "A 1
B 3
C 5
A 2
B 7
C 2"
input.scan(/[A-Z]+ \d+/)
.map{ |str| str.split(' ') }
.group_by{ |letter, _| letter }
.each do |letter, pairs|
print letter
print ' '
puts pairs.map{ |_, number| number }.sort.join(' ')
end
#=>
# A 1 2
# B 3 7
# C 2 5

More concise way of inserting a character between all characters in a string

I want to insert a character at every possible index of a string, including before the first element and after the last. Right now I'm doing:
result = []
result << c + str
result << str + c
for i in 0..str.length-2 do
result << (str[0..i] + c + str[i+1..-1])
end
Is there a way of doing this without having 2 special cases and having a loop from 0 to str.length - 2
EDIT
Sample output with '-' and 'hello':
["-hello", "h-ello", "he-llo", "hel-lo", "hell-o", "hello-"]
I'll assume you want ["-hello", "h-ello", "he-llo", "hel-lo", "hell-o", "hello-"], your question is not clear.
s = "hello"
(0..s.size).map { |i| s.clone.insert(i, "-") }
#=> ["-hello", "h-ello", "he-llo", "hel-lo", "hell-o", "hello-"]
For those that prefer a functional approach (I do):
(0..s.size).map { |i| (s[0...i] + "-" + s[i..-1]) }
#=> ["-hello", "h-ello", "he-llo", "hel-lo", "hell-o", "hello-"]

Resources