Ruby FizzBuzz using arrays, my logic seems right but it is getting an error - ruby

FizzBuzz, a classic problem, returns all the numbers up to N with a slight twist. If a number is divisible by 3, it is replaced with "fizz". If it's divisible by 5, it's replaced with "buzz". If it's divisible by both, it's replaced with "fizzbuzz"
I keep getting this Error message:
comparison of Fixnum with nil failed
Can someone explain this error message to me please? Also why is the code not working?
def fizz_buzz(n)
arr = (1..n).to_a
i = 0
while arr[i] < arr[n]
if i % 3 == 0 && i % 5 == 0
arr[i] = 'fizzbuzz'
elsif i % 3 == 0
arr[i] = 'fizz'
elsif i % 5 == 0
arr[i] = 'buzz'
else
arr[i] = i
i += 1
end
end
return arr
end
fizz_buzz(12)

Your conditions are just a bit off, give this a try:
def fizz_buzz(n)
arr = (1..n).to_a
i = 0
while i < n
if arr[i] % 3 == 0 && arr[i] % 5 == 0
arr[i] = 'fizzbuzz'
elsif arr[i] % 3 == 0
arr[i] = 'fizz'
elsif arr[i] % 5 == 0
arr[i] = 'buzz'
end
i+=1
end
return arr
end
Trying to access arr[n] puts you outside the bounds of the array which returns nil in Ruby.

You can update the code the ruby way, by using blocks and guards, I don't remember last time I used a while loop in ruby :)
Also, Array.new accepts a block as an argument which you can exploit to build your Array in a single step:
def fizz_buzz(n)
Array.new(n) do |index|
x = index + 1
case
when x % 3 == 0 && x % 5 == 0 then "fizzbuzz"
when x % 3 == 0 then "fizz"
when x % 5 == 0 then "buzz"
else x
end
end
end
Notice I used 1 as a base index and not 0, you can just remove x = index + 1 and replace x with index to have it working in a zero index base
A solution with a block instead of the while loop, and guards
def fizz_buzz(n)
arr = (1..n).to_a
0.upto(n - 1) do |i|
arr[i] = "fizzbuzz" and next if i % 3 == 0 && i % 5 == 0
arr[i] = "fizz" and next if i % 3 == 0
arr[i] = "buzz" if i % 5 == 0
end
arr
end

#brad-melanson beat me to the straight-forward answer to your question, so I'll share an answer which uses some common Ruby idioms (passing a range to the Array constructor and map), which simplify things, prevent you from having to do any iteration bookkeeping and prevent the possibility of off-by-one errors, out-of-bounds errors, etc.
def fizz_buzz(n)
Array(1..12).map do |n|
if n % 3 == 0 && n % 5 == 0
'fizzbuzz'
elsif n % 3 == 0
'fizz'
elsif n % 5 == 0
'buzz'
else
n
end
end
end
result = fizz_buzz 12
# result => [1, 2, "fizz", 4, "buzz", "fizz", 7, 8, "fizz", "buzz", 11, "fizz"]

Related

Algorithm Challenge number formatting problem

Invoice numbers are numeric only with any number of digits. To format one correctly, group the digits in group of three plus a group of any remainder, but never leave one digit by itself, unless it's a one digit number. Eg these are all correct formatting
123
12-34
6
783-907-23-45
And these are not
123-4
98-456
There's one more catch user input is passed directly to the function and you never know what characters users might type. Ignore any part of the input that is not digit
Invoice.format_number should always return a string
module Invoice
def self.format_number(str)
return ""
end
end
puts Invoice.format_number("ab1234")
What I have tried
1st approach
arr = []
str.chars.each do |elem|
val = elem =~ /\A[-+]?[0-9]*\.?[0-9]+\Z/
arr << elem if val == 0
end
num_of_digits = arr.length
pairs_of_two = 0
pairs_of_three = 0
if num_of_digits > 5
while num_of_digits > 0 do
break if num_of_digits <= 3
if num_of_digits >= 3 && (num_of_digits % 3 == 0 || num_of_digits % 3 == 2)
pairs_of_three += 1
num_of_digits -= 3
elsif num_of_digits % 2 == 0 || num_of_digits % 2 == 1
pairs_of_two += 1
num_of_digits -= 2
end
end
end
2nd approach
arr = []
str.chars.each do |elem|
val = elem =~ /\A[-+]?[0-9]*\.?[0-9]+\Z/
arr << elem if val == 0
end
len = arr.length - 1
if arr.length > 4
str = ""
i = 0
while i < len do
if arr[i..i+3].length == 4
str << arr[i..i+2].join + "-"
i += 3
elsif arr[i..i+2].length == 3
str << arr[i..i+1].join + "-"
i += 2
elsif arr[i..i+1].length == 2
str << arr[i..i+1].join
i += 2
elsif !arr[i].nil?
str << arr[i]
i += 1
end
end
puts str
else
if arr.length <= 3
puts arr.join
else
puts arr[0..1].join + "-" + arr[2..3].join
end
end
But none of them is correct
Here is the function invoice_number in python
def invoice_number(invoice):
s = ''.join(x for x in invoice if x <= '9' and x >= '0')
n = len(s)
if n <= 3:
return s
w = ''
i = 0
while i + 3 <= n:
for j in range(0, 3):
w += s[i + j]
i += 3
w += ('-')
m = n - i
if m == 0: return w[:-1]
if m == 1: return w[:m-3] + '-' + s[-2:]
return w + s[i:]
Testing
print(invoice_number('1234567'))
print(invoice_number('12345678'))
print(invoice_number('abc123456789'))
print(invoice_number('1234abc5678xyz9foobar'))
123-45-67
123-456-78
123-456-789
123-456-789
Eliminating non-digits is easy with re. For your format, the key is to figure our the "right" splitting indices.
Here is a try:
import re
def splits(n, k):
idx = [(i, min(n, i+k)) for i in range(0, n, k)]
if len(idx) > 1:
(a, b), (c, d) = idx[-2:]
if d - c < 2:
idx[-2:] = [(a, b - 1), (c - 1, d)]
return idx
def myformat(s):
s = re.sub(r'[^0-9]+', '', s)
parts = [s[a:b] for a, b in splits(len(s), 3)]
return '-'.join(parts)
Tests:
>>> myformat('123')
123
>>> myformat('1234')
12-34
>>> myformat('6')
6
>>> myformat('7839072345')
783-907-23-45
As the question was asked for ruby, adding solution for ruby. (The inspiration of the code is mostly from #yuri answer)
def format_invoice(invoice)
# only numbers are allowed
invoice = invoice.tr("^0-9","")
#puts invoice
return invoice if(invoice.length <= 3)
formatted_invoice = ''
i = 0
# Loop to divide the invoice in group of 3
while i + 3 <= invoice.length do
for j in 0..2 do
formatted_invoice += invoice[i + j]
end
i += 3
formatted_invoice += ('-')
end
m = invoice.length - i
return formatted_invoice[0..-2] if m == 0
return formatted_invoice[0..m-4] + '-' + invoice[-2..-1] if m == 1
return formatted_invoice + invoice[i..-1]
end
Testing
puts format_invoice('abc1') # 1
puts format_invoice('abc123') # 123
puts format_invoice('abc123A4') # 12-34
puts format_invoice('1234567') # 123-45-67
puts format_invoice('12345678') # 123-456-78
puts format_invoice('abc123456789') # 123-456-789
puts format_invoice('1234a#c5678xyz9foobar') # 123-456-789

Using `map` to solve 'FizzBuzz'

I tired to use map to solve fizzbuzz.
def fizzbuzz(n)
array =(1..n).to_a
array.map{|x|
if x % 3 == 0 && x % 5 == 0
x = 'FizzBuzz'
elsif x % 3 == 0
x = 'Fizz'
elsif x % 5 == 0
x = 'Buzz'
end
}
array
end
Somehow, it doesn't work. Do you know what's wrong?
Method map does not change the original array. Use the bang version map! instead.
Using map! as suggested by #tmc and some other changes try:
def fizzbuzz(n)
array =(1..n).to_a
array.map!{|x|
if x % 3 == 0 && x % 5 == 0
x = 'FizzBuzz'
elsif x % 3 == 0
x = 'Fizz'
elsif x % 5 == 0
x = 'Buzz'
else
x = x
end
}
p array
end
fizzbuzz(10) #=> [1, 2, "Fizz", 4, "Buzz", "Fizz", 7, 8, "Fizz", "Buzz"]
As you can see I've added a call to the method fizzbuzz with an argument of 10 which you can change. And I've used p to inspect the array as well as a final else statement.

Ruby: FizzBuzz not working as expected

I'm having trouble getting an IF statement to produce the results I think they should. I'm not sure why I cannot get the && ("and") conditional to work.
def fizzbuzz(n)
pool = []
(1..n).each do |x|
if x % 3 == 0
pool.push('Fizz')
elsif x % 5 == 0
pool.push('Buzz')
elsif x % 3 == 0 && x % 5 == 0
pool.push('FizzBuzz')
else
pool.push(x)
end
end
puts pool
end
fizzbuzz(10)
and they results
1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
I'm not sure what I'm doing wrong here.
Try this instead:
def fizzbuzz(n)
pool = []
(1..n).each do |x|
if x % 3 == 0 && x % 5 == 0
pool.push('FizzBuzz')
elsif x % 5 == 0
pool.push('Buzz')
elsif x % 3 == 0
pool.push('Fizz')
else
pool.push(x)
end
end
puts pool
end
When you use if/elsif/elsif/else, it will execute only one of this conditions at time. If x % 3 == 0, then that's it, ruby will no longer enter any of those conditions, that's why fizzbuzz will never be printed.
The if/else if/else branching only executes one of the code blocks. If a condition is true, then the following block is executed and the program will skip to the end of the if/else statements.
Here is another working version, which is a bit cleaner, but as Tiago Farias said, you wont get the 'fizzbuzz' message printed in a range from [1..10], because you don't have a value which will have the rest 0 for both % 3 and % 5, the closest will be 15.
def fizzbuzz(n)
#pool = []
(1..n).each do |x|
send_no x
end
puts #pool
end
def send_no x
return #pool << 'fizzbuzz' if x % 3 == 0 && x % 5 == 0
return #pool << 'fizz' if x % 3 == 0
return #pool << 'buzz' if x % 5 == 0
#pool << x
end
fizzbuzz(10)

How can I DRY this series of conditional statements?

I often find myself checking multiple conditions. How can I cut down on the number of lines used to achieve the same effect?
def super_fizzbuzz(array)
final = []
for num in array
if num % 15 == 0
final << 'FizzBuzz'
elsif num % 5 == 0
final << 'Buzz'
elsif num % 3 == 0
final << 'Fizz'
else
final << num
end
end
final
end
def super_fizzbuzz(array)
array.map do |num|
a = []
a << 'Fizz' if num % 3 == 0
a << 'Buzz' if num % 5 == 0
a.empty? ? num : a.join()
end
end
def super_fizzbuzz(array)
final = []
array.each do |num|
num % 15 == 0 ? final << 'FizzBuzz' : num % 5 == 0 ? final << 'Buzz' : num % 3 == 0 ? final << 'Fizz' : final << num
end
final
end
But your way is more readable.
def super_fizzbuzz(array)
array.map do |num|
case 0
when num % 15 then "FizzBuzz"
when num % 5 then "Buzz"
when num % 3 then "Fizz"
else num
end
end
end
This is slightly more complex, but reduces number of explicit coded conditionals to 2:
FIZZBUZZ = { 3 => 'Fizz', 5 => 'Buzz' }
def super_fizzbuzz(array)
array.map do |num|
fbs = FIZZBUZZ.select do |divisor,cat|
num % divisor == 0
end.values
fbs.empty? ? num : fbs.join
end
end
There is always the danger when coding for DRY that you take things too far. In this case, with only two overlapping categories, I think the above is a little unwieldy. However, add another category or two:
FIZZBUZZ = { 3 => 'Fizz', 5 => 'Buzz', 7 => 'Boom', 11 => 'Whizz' }
and it starts to look smarter.
Quote:
I think Fizz-Buzz is "hard" for some programmers because (#1) it doesn't fit into any of the patterns that were given to them in school assignments, and (#2) it isn't possible to directly and simply represent the necessary tests, without duplication, in just about any commonly-used modern programming language.
Source: c2.com Wiki
Another way:
def super_fizzbuzz(arr)
arr.map do |e|
s = ''
s << 'Fizz' if (e%3).zero?
s << 'Buzz' if (e%5).zero?
s = e if s.empty?
s
end
end
super_fizzbuzz [9, 25, 225, 31]
#=> ["Fizz", "Buzz", "FizzBuzz", 31]

Initialize Array with Values (Ruby)

I was wondering if there was a way to initialize this array in one line:
mulof = []
(1..1000).each {|i| mulof << i if (i % 3 == 0 || i % 5 == 0)}
Yes there is using Enumerable#select :
mulof = (1..1000).select { |i| i % 3 == 0 || i % 5 == 0 }

Resources