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
Related
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
My problem is asking to iterate over an array of items and delete every other item until I reach the end of the array when I should start iterating backwards and keep deleting every other item and so on back and forth until only one item is left in the array.
for example: 1,2,3,4,5, would become 1,3,5 and then 3
I understand that I shouldn't be deleting from the original array so I created another array that just keeps every other item while decreasing its size but I can't get it to work and end up with an infinite loop.
arr=*(1..10)
s_arr=[]
until s_arr.length==1
i=0
while i<arr.length
s_arr.push(arr[i])
i+=2
end
arr=s_arr.reverse
s_arr=arr
end
Thank you.
Each iteration, replace the array with a version with every other element dropped
arr = *(1..10)
while arr.length > 1
arr = arr.select.with_index { |_, i| i % 2 == 0 }.reverse
end
This doesn't match your example, but your example seems inconsistent in the first place. If [1,2,3,4,5] goes to [1,3,5], then the next step should be [1,5] instead of [3].
This is similar to #Max's answer, but implemented recursively.
def last_one_standing(arr)
return arr.first if arr.size == 1
last_one_standing arr.select.with_index { |_,i| i.even? }.reverse
end
(1..16).each { |i| puts "1..%d: %d" % [i, last_one_standing((1..i).to_a)] }
1..1: 1
1..2: 1
1..3: 3
1..4: 3
1..5: 1
1..6: 1
1..7: 3
1..8: 3
1..9: 9
1..10: 9
1..11: 11
1..12: 11
1..13: 9
1..14: 9
1..15: 11
1..16: 11
try something like this so you can take advantage of Ruby's power
result = []
array.each_with_index {|val,idx| result << val if ( idx % 2 == 0)}
Try this on your ruby console:
arr=*(1..10)
x = (Math::log(arr.length) / Math::log(3)).to_i
3**x
arr=*(1..10)
s_arr=[]
until s_arr.length == 1
i=0
while i < arr.length
s_arr.push(arr[i])
i+=2
end
arr=s_arr.reverse
if s_arr.length != 1
s_arr=[]
end
end
Your problem was setting the s_arr array the same as the arr. Then you must add a conditional to tell it to stop when it does reach 1 otherwise it'll become 0 instead.
EDIT:
arr=*(1..10)
s_arr=[]
until s_arr.length == 1
s_arr = []
i=0
while i < arr.length
s_arr.push(arr[i])
i+=2
end
arr=s_arr.reverse
end
even better solution.
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
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
I am trying to write a method that takes an array and returns trueif there is an element that occurs three times in a row or false if it doesn't. I can't think of the syntax. Would you use count? See the example below.
def got_three?(array)
end
got_three?([1,2,2,3,4,4,4,5,6]) would return true as 4 shows up three times in a row.
Toying with the new Ruby 2.3.0 method chunk_while:
def got_three?(array)
array.chunk_while(&:==).any?{|g| g.size >= 3}
end
With Enumerable#chunk:
def got_three?(xs)
xs.chunk(&:itself).any? { |y, ys| ys.size >= 3 }
end
Not so smart but a naive one (using a instead of array since it is long):
a.each_index.any?{|i| a[i] == a[i + 1] and a[i + 1] == a[i + 2]}
I assume you don't have any nil in the array.
An alternative which may be more performant (as per #sawa's comments)...
def got_three?(array)
(0..(array.count-2)).any?{|i|array[i] == array[1+1] && array[i] == array[i+2]}
end
Look, ma, no indices!
def ducks_in_a_row?(arr, n)
cnt = 0
last = arr.first
arr.each do |d|
if d==last
cnt += 1
return true if cnt==n
else
last = d
cnt = 1
end
end
false
end
ducks_in_a_row?([1,2,3,4,5,6,6,7,7,7], 3)
#=> true
def got_three?(array)
array.each_cons(3).map{|g|g.uniq.length == 1}.any?
end
or as #wandmaker suggests...
def got_three?(array)
array.each_cons(3).any?{|g|g.uniq.length == 1}
end
Here is my take on this problem - I tried to make it generic, hopefully efficient so that the loop terminates as soon as n-consecutive elements are found.
def got_consecutive?(array, n = 3)
case array.size
when 0...n
return false
when n
return array.uniq.size == n
else
array[n..-1].each_with_object(array[0...n]) do |i, t|
(t.uniq.size == 1 ? (break t) : (t << i).shift)
end.uniq.size == 1
end
end
p got_consecutive?([])
#=> false
p got_consecutive?([1,2])
#=> false
p got_consecutive?([1,2,2,3,2,3,3,3], 3)
#=> true
p got_consecutive?([1,2,2,3,2,3,3,3], 4)
#=> false
p got_consecutive?([1,2,2,3,2,3,3,3,3,3,4,4,4,4], 5)
#=> true
The code takes care of border cases first such as when array did not have n elements in which case answer is obviously false, and another one being when the array had only n elements - in which case just a uniqueness check would suffice.
For cases where array size is greater than n, the code uses Enumerable#each_with_object with the initial object being an array of n elements from the array - this array is used also as temporary work area to track n consecutive elements and perform a check whether all those elements are same or not.