Error accessing an array's value by it's index? - ruby

Trying to sum up all the numbers in an array. Example 10 + 20 + 30 should be 60.
def sum *arr
i=0
total=0
while i <= arr.count
total += arr[i]
i+=1
end
total
end
puts sum(10,20,30)
Why am I getting this error. This code looks like it should work to me. What am I doing wrong? Why wont it let me access the array value by it's index?
p8.rb:23:in `+': nil can't be coerced into Fixnum (TypeError)
from p8.rb:23:in `sum'
from p8.rb:29:in `<main>'

Change
while i <= arr.count
to
while i < arr.count
arr[arr.count] is always out of bounds.
Fyi a shorter way to write sum is:
def sum *arr
arr.inject(:+)
end

Matt's answer gives you the canonical Ruby way to sum, using inject. But if you're not ready to learn inject, at least save yourself the trouble of manually keeping track of array indexes (where your actual problem lies!) by using #each to iterate through the array like so:
def sum *arr
total = 0
arr.each do |x|
total += x
end
total
end
puts sum(10,20,30) # => 60

Matt's answer is both slick and correct, but you hit the error because ruby zero indexes arrays. So if you changed the count condition
while i <= arr.count - 1
your error would go away

Related

Why am I getting all these nils - Euler 3

I came up with this solution for Project Euler 3 although I got the answer I also get so many nils before it I can't figure out why? is there any way I can only get the answer.
Anyway here is my code
def factor(number)
max = []
(2...number).each do |x|
if number % x == 0
number = number/x
max << number
s = max[-2]
elsif number == 1
return[]
end
puts s
end
end
puts factor(600851475143)
max is an empty array. s = max[-2] is s =[][-2] for a long time. [][-2] returns nil.

How to write my own max function

I know that there is a max function from the Enumerable library in Ruby.
However, I'm trying to figure out how to write my own max method in which the largest number in an array is figured out.
How do I do that? I'm really at loss because when I Google it, all I keep getting is the max function itself.
Any help/advice would be helpful!
Another naive approach is -
list = [3,4,2,5,6,7,8,2,5,1,4,4,6]
def maximum(list)
len = list.size - 1
maximum = list[0]
for i in 1..len
if maximum < list[i]
maximum = list[i]
end
end
maximum
end
puts maximum(list)
# >> 8
Here is the graphical explanation(taken from this link) -
You have 2 approaches: Enumerable#each (imperative) or Enumerable#reduce (usually functional, depends on how you use it). I prefer functional solutions, so I'd write:
module Enumerable
def my_max
reduce { |current_max, x| x > current_max ? x : current_max }
end
end

I need help on understading: Write a method, sum which takes an array of numbers and returns the sum of the numbers

I've been studying ruby and have been taking some exercises to see how much I have learned and I've come across this:
Q: Write a method, sum which takes an array of numbers and returns the sum of the numbers.
The answer was provided for the problem but I don't understand why or how. I would like some help from anyone to explain them for me in simple terms so that I can understand this. Please keep in mind that I'm new to programming. Thank you.
A:
def sum(nums)
total = 0
i = 0
while i < nums.count
total += nums[i]
i += 1
end
# return total
total
end
Let me add some comments to see if this helps...
def sum(nums)
# initialize total to zero
total = 0
# initialize a counter to zero
i = 0
# while the counter is less than the size / count of the array...
while i < nums.count
# add the number at the array index of i to total
# this can also be written: total = total + nums[i]
total += nums[i]
# increment your counter, then loop
i += 1
end
# return total
total
end
The way to do this with ruby is
def sum (nums_array)
nums_array.inject(:+)
end
Equivalently, you could use reduce, which is an alias for inject.
Inject iterates over your array cumulatively applying any binary operation to every element, returning the accumulator (in this case, the sum of all the elements). You could also do something like
nums_array.inject(:-)
nums_array.inject(:*)
nums_array.inject(:%)
and etc.
The best place to test any Ruby method is in IRB or PRY on the command line, or, if you'd rather use something with a GUI and are working on a Mac, CodeRunner is great.
For more on inject / reduce (or any method you come across), the ruby docs are a great resource.
def sum(nums)
total = 0
i = 0 # i is set to `0`, as in Ruby array is 0 based.i will point to the
# first element in the array initially.
while i < nums.count # loop to iterate through the array till the last index come.
total += nums[i] # nums[i] is for accessing the element from the array at index i.
# and adding the value of total in previous iteration to current element
# of the array at i(or to the initial value of total,if it is the first iteration).
i += 1 # this move the i from current index to next index of the array.
end
# return total
total
end
i += 1 is called the syntactic sugar of i=i+1.Same is true for total += nums[i].
This is horrible. Whoever wrote doesn't understand the first thing about Ruby. He doesn't even understand much of programming, apparently. Just forget about it.
This is how a Rubyist or pretty much any other programmer would solve that problem:
def sum(nums)
nums.inject(0, :+)
end
Unlike the code that was provided to you, this doesn't use any concepts outside of some basic math. (Fold and Addition.)

Random number generator issues in Ruby

My intention here is just to fill up an array with numbers in order from 1, to a random number between 1 and 1000. However, after repeatedly running this code (about 50 times), the highest number I have gotten is 120, and only twice has it been over 100. The majority of my arrays were anywhere between 0 and 60. This behavior appears off to me. Am I doing something wrong?
my_array = []
i = 0
while i <= rand(1000)
my_array << i
i += 1
end
puts my_array.count
puts my_array
Your function is broken, because you're checking versus the random number. Do this:
(0..1000).collect{ rand(1000) }
This will return an array of one thousand random numbers.
Or, closer to your code:
my_array = []
i = 0
while i <= 1000
my_array << rand(1000)
i += 1
end
As per comment, what you want is:
(1..rand(1000))
(1..rand(1000)).to_a
The first results in a range, which is "easier to carry around", the second results in the populated array.
(Edit) Note:
(1..10) is inclusive - (1..10).to_a == [1,2,3,4,5,6,7,8,9,10]
(1...10) is partially exclusive - (1...10).to_a == [1,2,3,4,5,6,7,8,9] - it does not include the end of the array, but still includes the beginning.
It sounds like you want:
(1...rand(1000)).to_a
Additionally, I have amended my code to reflect what I was trying to accomplish initially. My problem was that every time I looped through my code I generated a new random number. Because of this, as 'i' incremented toward 1000 it became more and more likely that a random number would be generated that was lower than 'i'. My fix, while not as elegant as the solution above that I accepted, was to store the random number in a variable, BEFORE attempting to use it in a loop. Thanks again. Here is the amended code:
my_array = []
i = 0
g = rand(1000)
while i <= g
my_array << i
i += 1
end
puts my_array.count
puts my_array

optimize this ruby code

So this code will count the total number of pairs of numbers whose difference is K. it is naive method and I need to optimize it. suggestions?
test = $stdin.readlines
input = test[0].split(" ")
numbers = test[1].split(" ")
N = input[0]
K = input[1]
count = 0
for i in numbers
current = i.to_i
numbers.shift
for j in numbers
difference = (j.to_i - current).abs
if (difference == K)
count += 1
end
end
end
puts count
Would have been nice for you to give some examples of input and output, but I think this is correct.
require 'set'
def count_diff(numbers, difference)
set = Set.new numbers
set.inject 0 do |count, num|
set.include?(num+difference) ? count+1 : count
end
end
difference = gets.split[1].to_i
numbers = gets.split.map { |num| num.to_i }
puts count_diff(numbers, difference)
Untested, hopefully actual Ruby code
Documentation for Set: http://www.ruby-doc.org/stdlib/libdoc/set/rdoc/classes/Set.html
require 'set'
numbers_set = Set.new
npairs = 0
numbers.each do |number|
if numbers_set.include?(number + K)
npairs += 1
end
if numbers_set.include?(number - K)
npairs += 1
end
numbers_set.add(number)
end
Someone deleted his post, or his post was deleted... He had the best solution, here it is :
test = $stdin.readlines
input = test[0].split(" ")
numbers = test[1].split(" ")
K = input[1]
count = 0
numbers.combination(2){|couple| couple.inject(:-).abs == K ? count++}
puts count
You don't even need N.
I do not know Ruby so I'll just give you the big idea:
Get the list
Keep a boolean array (call it arr), marking off numbers as true if the number exists in the list
Loop through the list and see if arr[num-K] and/or arr[num+K] is true where num is a number in your list
This uses up quite a bit of memory though so another method is to do the following:
Keep a hash map from an integer n to an integer count
Go through your list, adding num+K and num-K to the hash map, incrementing count accordingly
Go through your list and see if num is in the hash map. If it is, increment your counter by count

Resources