In my first ruby hw assignment I have to make a FizzBuzz program. (If an int is / 3 its converted to a "Fizz" string, if divisible by 5, a "Buzz" string, and if by both a "FizzBuzz" string.) Lastly, if not divisible by either I need to convert the number itself to a string AND store it into an array in a method named main.
class FizzBuzz
def initialize(int_num, str_fb, value)
self.int_num = int_num
self.str_fb = str_fb
self.value = value
end
def int_num
#int_num
end
def int_num=(num)
raise TypeError if not num.is_a? Integer
#num = int_num
end
def str_fb
#str_fb
end
def str_fb=(name)
if int_num % 3 == 0
int_num = "Fizz"
elsif int_num % 5 == 0
int_num = "Buzz"
elsif int_num % 5 == 0 and int_num % 3 == 0
int_num = "FizzBuzz"
else
str_fb = int_num.to_s #Also needs to be stored in an array in main function
end
def main
ary =array.new
end
end
How do I store the newly converted int_num (now a string) into the array in main? Also, the code above is only one method of the class, but does it appear correct? Thanks, I am a full blown Ruby noob and appreciate all the help.
**Added full code. Still incomplete, as I need to create a getter and setter for value that loops 1-50.
To answer the original question, just put the variable you want to return inside an array into brackets:
def main
[str_fb]
end
Lots of other Ruby-specific improvements could be made here. For starters, you can replace the int_num and int_num= methods with a single line: attr_accessor :int_num. (You would need to move your raise to the constructor.)
You can replace the str_fb method with another single line: attr_reader :str_fb.
In the constructor, it is more common in Ruby to use instance variables to store input parameters. Consider which methods you want to leave public and which to make private. You can make instance methods (including attr_accessor and attr_reader private by adding a single line private and moving those methods below it.
So your code might look like this:
class FizzBuzz
attr_accessor :int_num
attr_reader :str_fb, :value
def initialize(int_num, str_fb, value)
raise TypeError unless int_num.is_a?(Integer)
#int_num = int_num
#str_fb = str_fb
#value = value
end
<public instance methods here>
private
<private instance methods here>
end
You can add items to an array using push method or << (shovel) operator. I prefer the latter.
Here is my take on your assignment:
module MyModule
class FizzBuzz
attr_reader :main
def initialize(range)
raise TypeError unless range.is_a? Range
#main = []
range.to_a.each do |i|
main << process(i)
end
end
private
def process(num)
if num % 5 == 0 && num % 3 == 0
"FizzBuzz"
elsif num % 3 == 0
"Fizz"
elsif num % 5 == 0
"Buzz"
else
num
end
end
end
end
Note that I don't have any getter and setter methods. This is for two reasons:
The by calling the attr_reader class method, I am setting up a public getter method for main in a much more elegant way
I don't see a need for any setter methods as passing in the range when I initialise the class would is sufficient
If you save this module in a file named my_module.rb and run the irb command you can interact with your class as follows:
➜ Code irb
irb(main):001:0> require "#{absolute_path_to_file}/my_module"
=> true
irb(main):004:0> include MyModule
=> Object
irb(main):005:0> FizzBuzz.new(1..15).main
=> [1, 2, "Fizz", 4, "Buzz", "Fizz", 7, 8, "Fizz", "Buzz", 11, "Fizz", 13, 14, "FizzBuzz"]
irb(main):006:0> FizzBuzz.new('abc').main
TypeError: TypeError
from /Users/username/Code/my_module.rb:6:in `initialize'
from (irb):6:in `new'
from (irb):6
from /Users/alexveldtman/.rbenv/versions/2.3.1/bin/irb:11:in `<main>'
UPDATE: After looking at the code again, I thought it would be cool to refactor the class a bit to get rid of those boring if statements.
module MyModule
class FizzBuzz
attr_reader :main
def initialize(range)
raise TypeError, 'not a Range' unless range.is_a? Range
#main = []
range.to_a.each do |i|
main << fizzbuzzerize(i){|fizzy_string, k| string_or_num(fizzy_string, k) }
end
end
private
def fizzbuzzerize(num)
yield [divisible_by_3?(num), divisible_by_5?(num)].compact.join, num
end
def string_or_num(string, num)
(string.length > 0 ? string : nil) || num
end
def divisible_by_3?(num)
num % 3 == 0 ? "Fizz" : nil
end
def divisible_by_5?(num)
num % 5 == 0 ? "Buzz" : nil
end
end
end
Note: the fizzbuzzerize method yields to a block in stead of returning a value
Related
This question already has answers here:
Ruby * operator before array [duplicate]
(2 answers)
Closed 3 years ago.
Sorry for the noob question, but what does this asterisk mean at the beginning of a range?
class Matrix
def initialize(matrix_string)
#matrix = matrix_string.split("\n").map do |row|
row.split.map(&:to_i)
end
#rows = rows.length
#cols = columns.length
end
def rows
#matrix
end
def columns
#matrix.transpose
end
# --->>***
def saddle_points
[*0...#rows].product([*0...#cols]).select do |coords|
saddle_point?(*coords)
end
# ***<----
end
private
def saddle_point?(row, col)
(#matrix[row][col] == rows[row].max) &&
(#matrix[row][col] == columns[col].min)
end
end
As it is stated in the documentation:
You can turn an Array into an argument list with * (or splat) operator:
arguments = [1, 2, 3]
my_method(*arguments)
The same might be done for Range:
arguments = 1..3
my_method(*arguments) # essentially the same as my_method(1, 2, 3)
Also splat operator is allowed before ranges inside the array declaration to implicitly convert Range to Array:
[*1..3]
#⇒ [1, 2, 3]
* unpacks an array by "splatting" its contents and converting it to an array.
As stated by the other answers, the * operator is used to turn an array into an argument list.
But what if the object is not an array, as in your case? Then Ruby will call #to_a on the object, if defined, and use the returned array instead. Otherwise, the object itself is used.
Here's the case where the object is neither an array nor does it define #to_a:
x = *4 # => [4]
If the object defines #to_a, it will be called and used, as in the case of Range:
x = *0..1 # => [0, 1]
To prove this, we can prepend a module to trace the call to #to_a:
module Trace
def to_a
puts "#to_a called"
super
end
end
Range.prepend(Trace)
x = *0..1
# prints "#to_a called"
# => [0, 1]
Note that Ruby will not call #to_a if your object already is of type Array.
We can use this on our custom types as well:
class Foo
def to_a
[1, 2, 3]
end
end
x = *Foo.new # => [1, 2, 3]
By the way, Ruby's nil also implements #to_a. This allows us to pass nil as an argument as if nothing was passed, because nil.to_a returns []:
def count(*args)
args.count
end
count # => 0
count(*0..1) # => 2
count(*nil) # => 0
This can be useful when you have a variable that could be nil that you pass to a method which has a default value:
def say_hi(name = "Jane")
puts "Hi, #{name}"
end
name = nil
say_hi(*name) # prints "Hi, Jane"
But if we remove NilClass#to_a:
NilClass.undef_method(:to_a)
say_hi(*name) # prints "Hi, "
I'm currently learning ruby and I wrote this piece of code :
def multi_gen
s = []
for i in (3..10)
if i%3 == 0 || i%5 == 0
s<<i
end
end
return s
end
puts multi_gen
def rec_sum(num_arr)
if num_arr == []
return 0
else
num_arr.first + rec_sum(num_arr.shift)
end
end
puts rec_sum(multi_gen)
That should return the sum of all 3 and 5 multiples up to 1000.
But I get an error :
myrbfile.rb:17:in `rec_sum': undefined method `first' for 3:Fixnum (NoMethodError)
from villani.rb:17:in `rec_sum'
from villani.rb:21:in `<main>'
But when I re-write it like this :
def multi_gen
s = []
for i in (3..10)
if i%3 == 0 || i%5 == 0
s<<i
end
end
return s
end
puts multi_gen
def rec_sum(num_arr)
if num_arr == []
return 0
else
num_arr[0] + rec_sum(num_arr[1..num_arr.last])
end
end
puts rec_sum(multi_gen)
I don't get the error.
So why is my first rec_sum functions interpretting my Array as a Fixnum in the first case?
The issue is in the recursive call:
rec_sum(num_arr.shift)
Array#shift returns the shifted element, not the remaining array. You should explicitly pass the array as an argument to recursive call:
rec_sum(num_arr[1..-1])
or
rec_sum(num_arr.tap(&:shift))
The latter would [likely] be looking too cumbersome for the beginner, but it’s a very common rubyish approach: Object#tap yields the receiver to the block, returning the receiver. Inside a block (num_arr.tap(&:shift) is a shorthand for num_arr.tap { |a| a.shift } we mutate the array by shifting the element out, and it’s being returned as a result.
mudasobwa already explained why using shift doesn't give the expected result. Apart from that, your code is somehow unidiomatic.
In multi_gen you are creating an empty array and append elements to it using a for loop. You rarely have to populate an array manually. Instead, you can usually use one of Ruby's Array or Enumerable methods to generate the array. select is a very common one – it returns an array containing the elements for which the given block returns true:
(1..1000).select { |i| i % 3 == 0 || i % 5 == 0 }
#=> [3, 5, 6, 9, 10, 12, ...]
In rec_sum, you check if num_arr == []. Although this works, you are creating an empty throw-away array. To determine whether an array is empty, you should call its empty?:
if num_arr.empty?
# ...
end
To get the remaining elements from the array, you use:
num_arr[1..num_arr.last]
which can be abbreviated by passing a negative index to []:
num_arr[1..-1]
There's also drop which might look a little nicer:
num_arr[0] + rec_sum(num_arr[1..-1])
# vs
num_arr.first + rec_sum(num_arr.drop(1))
Another option to get first and remaining elements from an array is Ruby's array decomposition feature (note the *):
def rec_sum(num_arr)
if num_arr.empty?
0
else
first, *remaining = num_arr
first + rec_sum(remaining)
end
end
You could also consider using a guard clause to return from the method early:
def rec_sum(num_arr)
return 0 if num_arr.empty?
first, *remaining = num_arr
first + rec_sum(remaining)
end
Writing recursive methods is great for learning purposed, but Ruby also has a built-in sum method:
multi_gen.sum #=> 234168
or – since you are using an older Ruby version – inject:
multi_gen.inject(0, :+) #=> 234168
[1,2,3,3] - [1,2,3] produces the empty array []. Is it possible to retain duplicates so it returns [3]?
I am so glad you asked. I would like to see such a method added to the class Array in some future version of Ruby, as I have found many uses for it:
class Array
def difference(other)
h = other.each_with_object(Hash.new(0)) { |e,h| h[e] += 1 }
reject { |e| h[e] > 0 && h[e] -= 1 }
end
end
A description of the method and links to some of its applications are given here.
By way of example:
a = [1,2,3,4,3,2,4,2]
b = [2,3,4,4,4]
a - b #=> [1]
a.difference b #=> [1,2,3,2]
Ruby v2.7 gave us the method Enumerable#tally, allowing us to replace the first line of the method with
h = other.tally
As far as I know, you can't do this with a built-in operation. Can't see anything in the ruby docs either. Simplest way to do this would be to extend the array class like this:
class Array
def difference(array2)
final_array = []
self.each do |item|
if array2.include?(item)
array2.delete_at(array2.find_index(item))
else
final_array << item
end
end
end
end
For all I know there's a more efficient way to do this, also
EDIT:
As suggested by user2864740 in question comments, using Array#slice! is a much more elegant solution
def arr_sub(a,b)
a = a.dup #if you want to preserve the original array
b.each {|del| a.slice!(a.index(del)) if a.include?(del) }
return a
end
Credit:
My original answer
def arr_sub(a,b)
b = b.each_with_object(Hash.new(0)){ |v,h| h[v] += 1 }
a = a.each_with_object([]) do |v, arr|
arr << v if b[v] < 1
b[v] -= 1
end
end
arr_sub([1,2,3,3],[1,2,3]) # a => [3]
arr_sub([1,2,3,3,4,4,4],[1,2,3,4,4]) # => [3, 4]
arr_sub([4,4,4,5,5,5,5],[4,4,5,5,5,5,6,6]) # => [4]
This question already has answers here:
Why am I getting objects printed twice?
(4 answers)
Closed 6 years ago.
Trying to call an array from a function to just assert_equal to make sure it is returning the intended string.
here is my function:
def array_mod
a = *(1..100)
a.each { |i| if i % 3 == 0 && i % 5 == 0; i = "fifteen" elsif i % 3 == 0; i = "three" elsif i % 5 == 0; i = "five" else i = i end }
end
and here is my attempt at calling it.
require "minitest/autorun"
require_relative "array_modulus.rb"
class TestArrayFunction < Minitest::Test
def test_array1
results = array_mod
assert_equal(100, results.length)
end
def test_array2
results = array_mod
assert_equal("three", results[2])
end
end
The test passes the results.length, but returns the "three" as 3, an integer.
I know I could create an array and do it like
def abc
arr = []
*(1..100) do |i|
if i % 3 == 0
i = "three"
else
i = I
end
But I was curious if I could do it with the previous way of writing it.
Sorry for any mistakes, I wrote this on my phone.
You want to to use map. Try this:
def array_mod
a = *(1..100)
a.map do |i|
if i % 3 == 0 && i % 5 == 0
"fifteen"
elsif i % 3 == 0
"three"
elsif i % 5 == 0
"five"
end
end
end
The value of a method is the last expression evaluated in the method. In your case, it is a.each {...}. This method always returns a.
Actually, it is not clear to me what you intended to do with the each block, as the only thing it does is changing the local variable i inside the block, which doesn't affect anything outside the block.
Hence, your method is equivalent to
def array_mod
(1..100).to_a
end
Given a number, my code should return all the even numbers between 1 and the number, and print them in the following format:
22
4444
666666
etc...
This is the code so far:
def pattern(n)
n == 1 ? "" : arr = (1..n).select {|i| i if i % 2 == 0}.each {|item| return (item.to_s * item)}
end
With any number greater than four, it will only return the following:
22
I think that this may have something to do with the return in the block. However, when using print or puts, this returns an individual array element as follows:
[2]
Ideas for a way around this so that I can achieve the desired output?
This code fixes your issue:
def pattern(n)
n == 1 ? "" : arr = (1..n).select {|i| i if i % 2 == 0}.map {|item| (item.to_s * item)}
end
Note that I'm using map instead of each, and I'm not using a return. The return meant that you didn't actually finish looping over the numbers... as soon as you got to 2 you returned from the function.
map is what you want if you want to build up a new array with the results.
EDIT
A little more cleanup:
def pattern(n)
n == 1 ? "" : (1..n).select {|i| i.even?}.map {|item| item.to_s * item}
end
The arr = is unnecessary. Your block in a select should just return true or false... you could also use just i % 2 == 0 in there, but even? happens to exist. Also, the parentheses around item.to_s * item are unnecessary.
EDIT 2
Per comments below, if you want a single string, maybe this is what you're looking for (added .join("\n")):
def pattern(n)
n == 1 ? "" : (1..n).select {|i| i.even?}.map {|item| item.to_s * item}.join("\n")
end
EDIT 3
When returning a string, you can also skip the n==1 special case, since joining an empty array will just return an empty string:
def pattern(n)
(1..n).select {|i| i.even?}.map {|item| item.to_s * item}.join("\n")
end
Your code doesn't work because it returns when it reaches the first value. See:
def pattern n
return "" if n == 1
(1..n).select { |i|
i if i % 2 == 0
}.each { |item|
return (item.to_s * item) # You are returning here!
}
end
As a suggestion, you could simplify your code to:
def pattern n
(2..n).step(2) { |n| puts n.to_s * n }
end
or --even better IMO-- you return an array with all results and let the caller decide what to do with it:
def pattern n
(2..n).step(2).map { |n| n.to_s * n }
end
Here is another way in which you can tackle the problem, by employing Integer#times:
def pattern n
(2..n).each do |i|
next if i.odd?
i.times { print i }
puts
end
end
pattern 8
#=>
# 22
# 4444
# 666666
# 88888888