Undefined method `%' for nil:NilClass (NoMethodError) Ruby on Rails - ruby-on-rails

Getting an undefined method error for '%' for nil:NilClass (NoMethodError)
Here's the simple function I have:
def oddball_sum(numbers)
i =0
arr = []
while i <= numbers.length
if numbers[i] % 2 != 0
arr << numbers[i]
end
i +=1
end
return arr.sum
end
Can't determine the issue; the method is supposed to take an array of integers and return the sum of all the odd elements.

Suppose that numbers is [1,2,3,4], when i increase to 4, numbers[4] will return nil
The condition should be i < numbers.length
Instead of using while, you can also use inject
numbers.inject(0) { |sum, i| i % 2 != 0 ? sum + i : sum }

You can go through arrays on the ruby docs ruby is one of the elegantly written languages. less code to achieve the same result. As follow the solution this would do
numbers.select {|num| num.odd? }.sum

Related

Cannot convert data from cookies

I want to make a method that will count the number of user actions on the site.
I have the code:
def actions_counter
if cookies[:"actions"] != nil
cookies[:"actions"].to_i += 1
else
cookies[:"actions"] = 0
end
end
But for some reason this does not work.
NoMethodError in PostsController#show
undefined method `to_i=' for "1":String Did you mean? to_i to_r to_f to_s to_d to_c
Just change your method to
def actions_counter
if cookies[:actions]
cookies[:actions] = cookies[:actions].to_i + 1
else
cookies[:actions] = 0
end
end
The issue is that Ruby understands cookies[:"actions"].to_i += 1 as
cookies[:"actions"].to_i = cookies[:"actions"].to_i + 1
# and this ^^^^^^^^ doesn't make sense

Why is initializing variables so important?

Please can someone explain to me, why NOT initializing first_idx and last_idx causes the code not to run??
When I run it I get this error "undefined local variable or method last_idx". I know that the advice is to always initialize the variables, but I don't understand why. After all first_idx and last_idx will ALWAYS get a value inside the loop because the argument letter is always present in the string (in this particular problem).
I'd really appreciate some (simple) insight. Thank you!
P.S, I also know that the problem is easily solved using #index and #rindex in Ruby, but I'm not allowed to solve it using straightforward methods.
def find_for_letter(string, letter)
first_idx = nil
0.upto(string.length - 1) do |idx1|
if string[idx1] == letter
first_idx = idx1
break
end
end
last_idx = nil
(string.length - 1).downto(0) do |idx2|
if string[idx2] == letter
last_idx = idx2
break
end
end
if last_idx == first_idx
return [first_idx]
else
return [first_idx, last_idx]
end
end
def first_last_indices(word)
h = {}
word.chars.each do |char|
h[char] = find_for_letter(word, char)
end
h
end
Variables in block
From the Ruby Programming Language:
Blocks define a new variable scope: variables created within a block
exist only within that block and are undefined outside of the block.
Be cautious, however; the local variables in a method are available to
any blocks within that method. So if a block assigns a value to a
variable that is already defined outside of the block, this does not
create a new block-local variable but instead assigns a new value to
the already-existing variable.
a = 0
2.times do
a = 1
end
puts a #=> 1
b = 0
2.times do |i;b| # <- b will stay a block-local variable
b = 1
end
puts b #=> 0
2.times do |i|
c = 1
end
puts c #=> undefined local variable or method `c' for main:Object (NameError)
Refactoring your code
Iterating with chars and index
Here's a smaller method for your goal.
It keeps a hash with minmax indices for each character.
The default hash value is an empty array.
The method iterates over each character (with index).
If minmax array already contains 2 values :
it replaces the second one (max) with current index.
it adds current index to the array otherwise.
def first_last_indices(word)
minmax_hash = Hash.new { |h, k| h[k] = [] }
word.each_char.with_index do |char, index|
minmax = minmax_hash[char]
if minmax.size == 2
minmax[1] = index
else
minmax << index
end
end
minmax_hash
end
p first_last_indices('hello world')
{"h"=>[0], "e"=>[1], "l"=>[2, 9], "o"=>[4, 7], " "=>[5], "w"=>[6], "r"=>[8], "d"=>[10]}
With group_by
Here's another possibility. It uses group_by to get all the indices for each character, and minmax to get just the first and last indices :
def first_last_indices(word)
word.each_char.with_index
.group_by{ |c, _| c }.map{ |c, vs|
[c, vs.map(&:last).minmax.uniq]
}.to_h
end
p first_last_indices('hello world')
{"h"=>[0], "e"=>[1], "l"=>[2, 9], "o"=>[4, 7], " "=>[5], "w"=>[6], "r"=>[8], "d"=>[10]}
Even if you do not declare last_idx, you can still initialise it inside the loop, i.e.:
(string.length - 1).downto(0) do |idx2|
if string[idx2] == letter
last_idx = idx2 # works absolutely fine
break
end
end
However notice where you declared the variable. Its a local variable and hence its tied to the block you are in. Now when you try to access that variable outside the block, you get the error:
undefined local variable or method last_idx
To make the variable available outside the block, you have to declare it outside. That is what you are doing when you declare last_idx = nil before the block where its assigned a value.
UPDATE:
Though by using instance variables you can avoid declaration, the best practices suggests it should be used in cases where information that these variables have is relevant to all or almost all of the class. On the other hand, if the information is very much limited to this particular method use local variables.
This is just the way that local variables work.
If you use instance variables, Ruby will assume that they have been initialised inside the conditional block, but will not for local variables.
def find_for_letter(string, letter)
0.upto(string.length - 1) do |idx1|
if string[idx1] == letter
#first_idx = idx1
break
end
end
(string.length - 1).downto(0) do |idx2|
if string[idx2] == letter
#last_idx = idx2
break
end
end
if #last_idx == #first_idx
return [#first_idx]
else
return [#first_idx, #last_idx]
end
end
This works fine.

Why am I getting an undefined method '<=' for in my ROR app

Question I have a custom divide and conquer array sorter that I would like to use. This all works well until I try to use it on an array in my controller I get this message.. NoMethodError (undefined method '<=' for #<Entry:0x0000000ac7d850>): Any help would be greatly appreciated thank you!
here is my Entry model with the mergesort method I am calling in my controller.
def self.mergesort(container)
return container if (container.size <= 1)
mid = container.size / 2
left = container[0...mid]
right = container[mid...container.size]
merge(mergesort(left), mergesort(right))
end
def self.merge(left, right)
sorted = []
until left.empty? or right.empty?
(left.first <= right.first) ? sorted << left.shift : sorted << right.shift
end
sorted + left + right
end
Here is my Entry controller where I am trying to call it.
def pending_sort
#ent_sort = Entry.where("section = ? and approve_disapprove = ?", #mgr_section, '3')
#ent = Entry.mergesort(#ent_sort)
end
You probably have a nil for the first element of either left or right.
irb(main):001:0> left = []
=> []
irb(main):002:0> right = [1]
=> [1]
irb(main):003:0> left.first
=> nil
irb(main):004:0> left.first <= right.first
NoMethodError: undefined method `<=' for nil:NilClass
from (irb):4
from /usr/bin/irb:11:in `<main>'
You can fix the error by casting the nil to a different value. For example, if the values you are comparing are always integers you can change the following line:
(left.first <= right.first) ? sorted << left.shift : sorted << right.shift
to this:
(left.first.to_i <= right.first.to_i) ? sorted << left.shift : sorted << right.shift
But think about how it will affect your functionality... it may break something if it isn't what you actually want to do.

Rails method for having a safe sum for enumerable

Rails provides a very convenient construct for allowing the summation of elements of an array.
foo = [1,2,3]
foo.sum
=> 6
However, you aren't always sure if your array will have non nil values, in that case the above method fails
bar = [1,2,nil,4]
bar.sum
TypeError: nil can't be coerced into Fixnum
Is there anything like a safe_sum that ignores the nil values?
You can use Array#compact method:
bar.compact.sum
# => 7
or, you can go with Enumerable#inject:
bar.inject(0) { |sum, el| el ? sum + el : sum }
# => 7
Although the first approach looks much prettier and more convenient to me.
You can even use compact
bar.compact.sum
module Enumerable
def safe_sum(identity = 0, &block)
if block_given?
map(&block).compact.sum(identity)
else
reduce { |sum, element| sum + element.to_f } || identity
end
end
end

LocalJumpError (Ruby on Rails)

I have searched/Googled around but I'm struggling with the following problem.
I am building a Rails 2.3.2 application and one of the requirements is to calculate the median of an array of results. I am using code for calculating the median from the Ruby Cookbook but keep running in to a problem with receiving an error 'LocalJumpError - no block given' when I attempt to find the median of an array where there are an odd number of members.
The example code in my view is as follows:
<%= survey_response.median([6,4,5,4,4,2]) %>
Then in survey_response.rb model the methods are as follows:
def mean(array)
array.inject(array.inject(0) { |sum, x| sum += x } / array.size.to_f)
end
def median(array,already_sorted=false)
return nil if array.empty?
array = array.sort unless already_sorted
m_pos = array.size / 2
return array.size % 2 == 1 ? array[m_pos] : mean(array[m_pos-1..m_pos])
end
The error is caused when the median method refers back to the mean method to get the media of an odd total of items in the array. I just can't figure out why I get that error or indeed how to fix it - so I'd hugely appreciate any help/guidance/laughing anybody could offer me!
Thanks
Simon
Lis looks like it's due to you using a fractional index into the array. Try replacing:
m_pos = array.size / 2
with:
m_pos = (array.size / 2).ceil
Also, try changing your mean function to this:
def mean(array)
array.inject(0) { |sum, x| sum += x } / array.size.to_f
end
That mean method looks horribly botched. Try this:
def mean(array)
a.inject(0) { |sum,x| sum += x } / a.size.to_f
end
Better code:
def mean(array)
array.inject { |sum, n| sum + n } / array.length.to_f
end
def median(array)
return nil if array.empty?
array.sort!
middle = array.length / 2
(array.length % 2 == 1) ? array[middle] : mean([array[middle-1], array[middle]])
end
puts median([5,11,12,4,8,21]) # => 9.5

Resources