error in modulus method in ruby - ruby

I am trying to write a method that takes in an array as an argument and returns an array of the numbers in the argument that are have both an even index number and an even value. I am not sure why, but it is giving me the error "undefined method %" in line 5. Can someone explain how I can fix this?
def odd_value_and_position(array)
newArray=[] #create new array
i=0 #i=0
while i <= array.length #loop while
newArray.push(array[i]) if array[i] % 2 != 0
i = i + 2
end
return newArray
end
puts odd_value_and_position([0,1,2,3,4,5])

Another way to do this:
def evens arr
arr.select.with_index { |e,i| e.even? && i.even? }
end
evens [0,1,2,3,4,5] #=> [0,2,4]

When i is equal to array.length, array[i] is nil.
What is nil % 2? It is undefined.
def odd_value_and_position(array)
newArray=[] #create new array
i=0 #i=0
while i < array.length #loop while
newArray.push(array[i]) if array[i] % 2 != 0
i = i + 2
end
return newArray
end
puts odd_value_and_position([0,1,2,3,4,5]) #=> []
puts odd_value_and_position([1,2,3,4,5]) #=> [1,3,5]
Due to the fact that the first element in a Ruby Array has 0 as index, I'm not sure you get the result you expected. See examples in code.
A more Rubyish example would be :
def odd_value_and_position(array)
array.select.with_index(1){|x,i| x.odd? && i.odd?}
end
puts odd_value_and_position([1,2,3,4,5]) #=> [1,3,5]

If I understand the question right, I'd go with something like:
def some_method_name(array)
array.select.with_index { |*ij|
ij.all?(&:even?)
}
end
puts some_method_name([0, 1, 2, 3, 4, 5, 10, 13, 21, 22, 30])
# >> 0
# >> 2
# >> 4
# >> 10
# >> 30
Here's what it's doing:
def some_method_name(array)
array.select.with_index { |*ij|
ij # => [0, 0], [1, 1], [2, 2], [3, 3], [4, 4], [5, 5], [10, 6], [13, 7], [21, 8], [22, 9], [30, 10]
ij.all?(&:even?) # => true, false, true, false, true, false, true, false, false, false, true
}
end
puts some_method_name([0, 1, 2, 3, 4, 5, 10, 13, 21, 22, 30])
# >> 0
# >> 2
# >> 4
# >> 10
# >> 30
There are a couple problems with the original code.
Using while loops easily leads to problems with off-by-one errors, or loops that never trigger, or loops that never end.
To combat that in Ruby, we use each and map, select, reject or similar iterators to loop over the array, and process each element in turn, then base the logic on that.
array.select is looking at each element and applying the logic in the block, looking for "truthy" results. with_index adds the index of the iteration as a second value passed into the block. *id turns the two values being passed in into an array, making it easy to apply all? and its even? test. If even? returns true to both then all? triggers and returns true again which signals to select to return that element of the array.

Related

Ruby program to create even_odd method that accepts the whole number

I need help to write even_odd method that accepts an array of whole number.
It should return an array of 2 arrays
The first nested array should contain only the odd numbers
The second nested array should contain only the even numbers
If there are no even or odd numbers, the respective inner array should be empty
Output should look like this : -
even_odd([3, 5, 8, 2, 4, 6])
[[3, 5], [2, 4, 6, 8]]
even_odd([3, 5])
[[3, 5], []]
even_odd([2, 4])
[[], [2, 4]]
I am new to ruby programming, I have tried below but not getting the result :-
def even_odd(numbers)
arr1, arr2 = []
idx = 0
while idx < numbers.length
if numbers[idx] % 2 == 0
puts arr1[idx]
elsif
puts arr2[idx]
end
idx += 1
end
end
puts even_odd([2, 3, 6])
Error :-
main.rb:6:in `even_odd': undefined method `[]' for nil:NilClass (NoMethodError)
from main.rb:13:in `<main>'
I would do this
def even_odd(numbers)
numbers.sort.partition(&:odd?)
end
even_odd([3, 5, 8, 2, 4, 6])
# => [[3, 5], [2, 4, 6, 8]]
even_odd([3, 5])
# => [[3, 5], []]
even_odd([2, 4])
# => [[], [2, 4]]
puts is a print statement in ruby, not an append one. It also doesn't run a function/method. You'll also want to call the index on the numbers array inside the if...else block.
This should do the trick:
def even_odd(numbers)
arr1, arr2 = [], []
idx = 0
while idx < numbers.length
if numbers[idx] % 2 == 0
arr1 << numbers[idx]
elsif
arr2 << numbers[idx]
end
idx += 1
end
return arr1, arr2
end
arrays = even_odd([2, 3, 6])
puts arrays

List the factors of a non-prime number

My goal is to display if a number is prime, and if it is not, list all of its factors. If I input 6, the program should return:
6 is not a prime number => 2, 3
Here is what I have so far:
puts "Enter a number: "
num = gets
num = num.to_i
def prime(num)
is_prime = true
for i in 2..num-1
if num % i == 0
is_prime = false
end
end
if is_prime
puts "#{num} is prime!"
else
puts "#{num} is not prime!"
end
end
prime(num)
I tried making a while loop for this but I can't seem to make it work. I'm not sure if I am using the num%i==0 formula correctly. Any help would be appreciated.
If permitted to do so, use the Standard Library class Prime.
require 'prime'
def is_prime? n
n.prime? ? n : Prime.prime_division(n)
end
p is_prime? 6 #=> [[2, 1], [3, 1]]
p is_prime? 11 #=> 11
p is_prime? 100 #=> [[2, 2], [5, 2]]
Homework hint 1)
You need to change the following things in order to get all the factors:
do not stop as soon as you find one factor
(which your prime-finder code inefficiently does not do anyway...)
print each factor you find
If you also need the correct exponent you further need to
check for each found factor whether it is still a factor after dividing by it,
this needs to be done in a loop
Homework hint 2)
In order to get a specific desired output, print the decoration (e.g. "," and newlines) explicitly when it is needed.
Printing the statement first and only once can be done by using a flag for "already printed" and checking it before printing the statement in the loop.
(I assume that this is a homework question and am therefor intentionally not giving a complete solution, according to the compromise desccribed here How do I ask and answer homework questions?
Thank you for not minding my assumption, or so I gather from your accepting my answer. In case it is not about homework I assume that you appreciated the help and the way of helping, in spite of my mistake.)
require 'prime'
def factors(n)
first, *rest =
Prime.prime_division(n).map { |n,power| [n].product((0..power).to_a) }
first.product(*rest).map { |a| a.reduce(1) { |t,(n,power)| t * n**power } }
end
Let's use this method to compute the factors of 1500.
factors 1500
#=> [ 1, 5, 25, 125,
# 3, 15, 75, 375,
# 2, 10, 50, 250,
# 6, 30, 150, 750,
# 4, 20, 100, 500,
# 12, 60, 300, 1500]
Note that
Prime.prime_division(1500)
#=> [[2, 2], [3, 1], [5, 3]]
and
Prime.prime_division(500).map { |n,power| [n].product((0..power).to_a) }
#=> [[[2, 0], [2, 1], [2, 2]], [[5, 0], [5, 1], [5, 2], [5, 3]]]
Moreover, factors(n).size equals 2 if and only if n is prime. For example,
factors(37)
#=> [1, 37]
Prime.prime?(37)
#=> true
See Prime::prime_division.

Checking arrays and implementing bool methods

You have an array. If any two numbers add to zero in the array, return true. It doesn't matter how many pairs there are—as long as there is one pair that adds to zero, return true. If there is a zero, it can only return true if there is more than one.
I wrote two functions, one to check for each, and a final one to combine both, and return false if either aren't met.
def checkZero(array)
zerocount = 0
for j in 0..array.count
if array[j] == 0
zerocount += 1
end
end
if zerocount > 1 #this part seems to not be working, not sure why
return true
else
return false
end
end
def checkNegative(array)
for j in 0..array.count
neg = -array[j] #set a negative value of the current value
if array.include?(neg) #check to see whether the negative exists in the array
return true
else
return false
end
end
end
def checkArray(array)
if checkZero(array) == true or checkNegative(array) == true
return true
else
return false
end
end
Then run something like
array = [1,2,3,4,0,1,-1]
checkArray(array)
So far, Ruby isn't returning anything. I just get a blank. I have a feeling my return isn't right.
The problem may be that you didn't output the result.
array = [1,2,3,4,0,1,-1]
puts checkArray(array)
The checkArray method can be written like the following, if performance (O(n^2)) is not a great concern:
def check_array(array)
array.combination(2).any?{|p| p.reduce(:+) == 0}
end
The more efficient (O(n log n)) solution is:
def check_array(array)
array.sort! # `array = array.sort` if you need the original array unchanged
i, j = 0, array.size - 1
while i < j
sum = array[i] + array[j]
if sum > 0
j -= 1
elsif sum < 0
i += 1
else
return true
end
end
return false
end
Here's are a few relatively efficient ways to check if any two values sum to zero:
Solution #1
def checkit(a)
return true if a.count(&:zero?) > 1
b = a.uniq.map(&:abs)
b.uniq.size < b.size
end
Solution #2
def checkit(a)
return true if a.sort_by(&:abs).each_cons(2).find { |x,y| x == -y }
false
end
Solution #3
def checkit(a)
return true if a.count(&:zero?) > 1
pos, non_pos = a.group_by { |n| n > 0 }.values
(pos & non_pos.map { |n| -n }).any?
end
Solution #4
require 'set'
def checkit(a)
a.each_with_object(Set.new) do |n,s|
return true if s.include?(-n)
s << n
end
false
end
Examples
checkit([1, 3, 4, 2, 2,-3,-5,-7, 0, 0]) #=> true
checkit([1, 3, 4, 2, 2,-3,-5,-7, 0]) #=> true
checkit([1, 3, 4, 2,-3, 2,-3,-5,-7, 0]) #=> true
checkit([1, 3, 4, 2, 2,-5,-7, 0]) #=> false
Explanations
The following all refer to the array:
a = [1,3,4,2,2,-3,-5,-7,0]
#1
Zeroes present a bit of a problem, so lets first see if there are more than one, in which case we are finished. Since a.count(&:zero?) #=> 1, a.count(&:zero?) > 1 #=> false, so
return true if a.count(&:zero?) > 1
does not cause us to return. Next, we remove any duplicates:
a.uniq #=> [1, 3, 4, 2, -3, -5, -7, 0]
Then convert all the numbers to their absolute values:
b = a.uniq,map(&:abs) #=> [1, 3, 4, 2, 3, 5, 7, 0]
Lastly see if c contains any dups, meaning the original array contained at least two non-zero numbers with opposite signs:
c.uniq.size < c.size #=> true
#2
b = a.sort_by(&:abs)
#=> [0, 1, 2, 2, 3, -3, 4, -5, -7]
c = b.each_cons(2)
#=> #<Enumerator: [0, 1, 2, 2, 3, -3, 4, -5, -7]:each_cons(2)>
To see the contents of the enumerator:
c.to_a
#=> [[0, 1], [1, 2], [2, 2], [2, 3], [3, -3], [-3, 4], [4, -5], [-5, -7]]
c.find { |x,y| x == -y }
#=> [3, -3]
so true is returned.
#3
return true if a.count(&:zero?) > 1
#=> return true if 1 > 1
h = a.group_by { |n| n > 0 }
#=> {true=>[1, 3, 4, 2, 2], false=>[-3, -5, -7, 0]}
b = h.values
#=> [[1, 3, 4, 2, 2], [-3, -5, -7, 0]]
pos, non_pos = b
pos
#=> [1, 3, 4, 2, 2]
non_pos
#=> [-3, -5, -7, 0]
c = non_pos.map { |n| -n }
#=> [3, 5, 7, 0]
d = pos & c
#=> [3]
d.any?
#=> true
#4
require 'set'
enum = a.each_with_object(Set.new)
#=> #<Enumerator: [1, 3, 4, 2, 2, -3, -5, -7, 0]:each_with_object(#<Set: {}>)>
enum.to_a
#=> [[1, #<Set: {}>],
# [3, #<Set: {}>],
# ...
# [0, #<Set: {}>]]
Values are passed into the block, assigned to the block variables and the block is executed, as follows:
n, s = enum.next
#=> [1, #<Set: {}>]
s.include?(-n)
#=> #<Set: {}>.include?(-1)
#=> false
s << n
#=> #<Set: {1}>
n, s = enum.next
#=> [3, #<Set: {1}>]
s.include?(-3)
#=> false
s << n
#=> #<Set: {1, 3}>
...
n, s = enum.next
#=> [2, #<Set: {1, 3, 4, 2}>]
s.include?(-n)
#=> false
s << n
#=> #<Set: {1, 3, 4, 2}> # no change
n, s = enum.next
#=> [-3, #<Set: {1, 3, 4, 2}>]
s.include?(-n)
#=> true
causing true to be returned.
I can’t reproduce any problem with your code, but you can express the solution very succinctly using combination to get all possible pairs, then summing each pair with reduce, and finally checking if any are zero?:
[1,2,3,4,0,1,-1].combination(2).map { |pair| pair.reduce(:+) }.any?(&:zero?)
This is a bit of a code review. Let's start with the first method:
def checkZero(array)
Ruby naming convention is snake_case rather than camelCase. This should be def check_zero(array)
Now the loop:
zerocount = 0
for j in 0..array.count
if array[j] == 0
zerocount += 1
end
end
As #AndrewMarshall said, for is not idiomatic. each is preferable. However, in ruby initializing a variable before a loop is almost never needed thanks to all the methods available to you on Array and Enumerable (which is included in Array). I highly recommend committing these methods to memory. The above can be written
array.any? {|number| number.zero?}
or equivalently
array.any?(&:zero?)
Now, this part:
if zerocount > 1 #this part seems to not be working, not sure why
return true
else
return false
end
end
Whenever you have the pattern
if (expr that returns true or false)
return true
else
return false
end
it can be simplified to simply return (expr that returns true or false). And you can even omit the return if it is the last statement of a method.
Putting it all together:
def check_zero(array)
array.any?(&:zero?)
end
def check_zero_sum(array)
array.combination(2).any?{|a,b| a + b == 0}
end
def check_array(array)
check_zero(array) || check_zero_sum(array)
end
(Note I borrowed AndrewMarshall's code for check_zero_sum which I think is easy to follow, but #CarySwoveland's answer will be faster)
Edit
I missed the fact that check_zero isn't even necessary because you want at least a pair, in which case check_zero_sum is all you need.
def check_array(array)
array.combination(2).any?{|a,b| a + b == 0}
end

Get total number of ranges of a given length in an array

I have an array total of 12 elements, each element represents and int. For instance total[0] = 1. I have another array remaining that is total - occupied spaces. remaining will have fewer elements that total.
I want to write a method that can look in total for instances where there are >= size gaps between consecutive ints in the array. For example:
If `foo.total = [1,2,6,7,8,9,]`
then when I call `foo.number_of_slots_available(3)`
I get `2` (because 3,4,5 is not included and 10,11,12 is not included)
Here are the beginnings of my method:
def number_of_slots(size)
total_array = (1..12).to_a
occupied_spaces = some_map.to_a
remaining_array = total_array - occupied_spaces
return ????
end
Enumerable#chunk is the good way to go. Look below.
arr = [1,2,6,7,8,9]
rng = (1..12).to_a
rng.chunk { |i| arr.include? i }.to_a
# => [[true, [1, 2]],
# [false, [3, 4, 5]],
# [true, [6, 7, 8, 9]],
# [false, [10, 11, 12]]]
rng.chunk { |i| arr.include? i }.count{|j| j[0] == false}
# => 2
Edit
"I want to write a method that can look in total for instances where there are >= size gaps between consecutive ints in the array"
arr = [1,2,3,6,7,8,9,10,11]
rng = (1..15).to_a
rng.chunk { |i| arr.include? i }.to_a
# => [[true, [1, 2, 3]],
# [false, [4, 5]],
# [true, [6, 7, 8, 9, 10, 11]],
# [false, [12, 13, 14, 15]]]
rng.chunk { |i| arr.include? i }.count{|j| j[0] == false and j[1].size >= 3 }
# => 1
rng.chunk { |i| arr.include? i }.count{|j| j[0] == false and j[1].size >= 2 }
# => 2
# J[1] is the array,whose size is the actual gap size.
If total is sorted is a simple algorithm and should be something like this (I might have some syntax errors):
def containsGaps(total, gap)
int i = 0;
int count = 0;
while i < total.length -1 do
if total[i+1] - total[i] >= gap then count++;
end
return count;
end
And your return might be:
return containsGaps(total_array, size);
Here is a way I found of doing it. I modified the method a bit adding in the array to be passed along with the size.
#!/usr/bin/ruby
arr = [1,2,6,7,8,9]
bar = [1,2,3,6,7,10]
def number_of_slots(arr, size)
count = 0
range = (1..12).to_a
# arr.sort! (if the array is not always sorted)
range.each_cons(size) do |chunk|
if (chunk & arr).size == 0
count += 1
end
end
count
end
puts number_of_slots(arr, 3)
puts number_of_slots(bar, 2)
Output:
2
3

Create two-dimensional arrays and access sub-arrays in Ruby

I wonder if there's a possibility to create a two dimensional array and to quickly access any horizontal or vertical sub array in it?
I believe we can access a horizontal sub array in the following case:
x = Array.new(10) { Array.new(20) }
x[6][3..8] = 'something'
But as far as I understand, we cannot access it like this:
x[3..8][6]
How can I avoid or hack this limit?
There are some problems with 2 dimensional Arrays the way you implement them.
a= [[1,2],[3,4]]
a[0][2]= 5 # works
a[2][0]= 6 # error
Hash as Array
I prefer to use Hashes for multi dimensional Arrays
a= Hash.new
a[[1,2]]= 23
a[[5,6]]= 42
This has the advantage, that you don't have to manually create columns or rows. Inserting into hashes is almost O(1), so there is no drawback here, as long as your Hash does not become too big.
You can even set a default value for all not specified elements
a= Hash.new(0)
So now about how to get subarrays
(3..5).to_a.product([2]).collect { |index| a[index] }
[2].product((3..5).to_a).collect { |index| a[index] }
(a..b).to_a runs in O(n). Retrieving an element from an Hash is almost O(1), so the collect runs in almost O(n). There is no way to make it faster than O(n), as copying n elements always is O(n).
Hashes can have problems when they are getting too big. So I would think twice about implementing a multidimensional Array like this, if I knew my amount of data is getting big.
rows, cols = x,y # your values
grid = Array.new(rows) { Array.new(cols) }
As for accessing elements, this article is pretty good for step by step way to encapsulate an array in the way you want:
How to ruby array
You didn't state your actual goal, but maybe this can help:
require 'matrix' # bundled with Ruby
m = Matrix[
[1, 2, 3],
[4, 5, 6]
]
m.column(0) # ==> Vector[1, 4]
(and Vectors acts like arrays)
or, using a similar notation as you desire:
m.minor(0..1, 2..2) # => Matrix[[3], [6]]
Here's a 3D array case
class Array3D
def initialize(d1,d2,d3)
#data = Array.new(d1) { Array.new(d2) { Array.new(d3) } }
end
def [](x, y, z)
#data[x][y][z]
end
def []=(x, y, z, value)
#data[x][y][z] = value
end
end
You can access subsections of each array just like any other Ruby array.
#data[0..2][3..5][8..10] = 0
etc
x.transpose[6][3..8] or x[3..8].map {|r| r [6]} would give what you want.
Example:
a = [ [1, 2, 3, 4, 5],
[6, 7, 8, 9, 10],
[11, 12, 13, 14, 15],
[21, 22, 23, 24, 25]
]
#a[1..2][2] -> [8,13]
puts a.transpose[2][1..2].inspect # [8,13]
puts a[1..2].map {|r| r[2]}.inspect # [8,13]
I'm quite sure this can be very simple
2.0.0p247 :032 > list = Array.new(5)
=> [nil, nil, nil, nil, nil]
2.0.0p247 :033 > list.map!{ |x| x = [0] }
=> [[0], [0], [0], [0], [0]]
2.0.0p247 :034 > list[0][0]
=> 0
a = Array.new(Array.new(4))
0.upto(a.length-1) do |i|
0.upto(a.length-1) do |j|
a[i[j]] = 1
end
end
0.upto(a.length-1) do |i|
0.upto(a.length-1) do |j|
print a[i[j]] = 1 #It's not a[i][j], but a[i[j]]
end
puts "\n"
end
Here is the simple version
#one
a = [[0]*10]*10
#two
row, col = 10, 10
a = [[0]*row]*col
Here is an easy way to create a "2D" array.
2.1.1 :004 > m=Array.new(3,Array.new(3,true))
=> [[true, true, true], [true, true, true], [true, true, true]]

Resources