Undefined method `keys' for []:Array - ruby-on-rails

I'm getting a Undefined method error:
undefined method `keys' for ["71", "74", "340", "75"]:Array
I'm using Gruff with Prawn to insert an image graph, I have the bar graph displaying properly, but the undefined error is occurring on the label call.
Using prawn (0.15.0) and gruff (0.5.1)
Prawn
def initialize(result)
super()
#result = result
show_graph
end
def show_graph
lint = #result.map {|v| v.lint/227 }
g = Gruff::Bar.new('540x200')
g.data(:lint, lint, '#00463f')
#result.each_with_index do |v, i|
g.labels = {i => v.variety.variety_name}
end
g.y_axis_label = 'Yield (bales/ha)'
g.marker_font_size = 16
g.marker_count = 5
g.theme = {:marker_color => '#333333', :font_color => '#333333', :background_colors => %w(#ffffff #ffffff)}
g.minimum_value = 0
g.hide_legend = true
g.write("#{Rails.root}/app/assets/images/chart.png")
image "#{Rails.root}/app/assets/images/chart.png"
end
Controller
#result = Result.where('trial_id' => params[:trial_id]).order('lint DESC')

#result.map returns an array. A Ruby Hash indeed works like a mathematical "map", but another name for Ruby's general .map method is .collect, because that's all it does. Array in, array out.
I don't know what g.labels needs, but you can get a hash with #result.inject({}){|v, h| h.merge v.variety_id => v.variety }. Change the argument to .merge to fit your needs.

Related

Ruby rails object is nil, yet controller logic acting as if object is not nil

I have an an object Search with a method listings that returns an array of hashes if there are search results, otherwise it returns an empty array. In the event there is any empty array, I need to skip some code and go straight to the show page. I've tried object.nil? object.empty? object.present? all with the same outcome....the object which is supposed to be nil is treated as non-nil.
Controller code:
def show
#search = Search.find(params[:id])
#results = #search.listings
if #results.last.present?
if #results.last[0] == "p" || #results.last[0] == "s" || #results.last[0] == "d"
p "#results.pop is either p, s, or d"
#sort_column = #results.pop
#grade = #sort_column.gsub(/[^0-9,.]/, "") unless #results.last[0] == "d"
end
end
end
show action results in
undefined method `[]' for nil:NilClass
Extracted source (around line #21):
19 p "#results.pop is either p, s, or d"
20 #sort_column = #results.pop
21 #grade = #sort_column.gsub(/[^0-9,.]/, "") unless #results.last[0] == "d"
22 end
23 end
24 end
but, the server interface verifies that #results.last is nil:
>> #results
=> []
>> #results.last
=> nil
>> #results.last.present?
=> false
>> #results.last[0]
NoMethodError: undefined method `[]' for nil:NilClass
from /Users/tomb/Projects/schoolsparrow/app/controllers/searches_controller.rb:21:in `show'
>>
I'm at a loss as to how to logic is getting past the results.last.present? when results.last is nil.
If you're testing to see whether or not your array has any values in it, try:
#results.any?
An empty array is not nil, which is why your checks are failing.
EDIT:
A concise explanation of nil v. empty v. blank in Ruby on Rails
Why don't you check your condition on #results.present? and not #results.last.present?.
#results.last would throw a NoMethodError if #result is nil
To check if an array has elements use .any? or its opposite twin .empty?:
irb(main):006:0> [].empty?
=> true
irb(main):007:0> [].any?
=> false
.present? and .presence work on arrays but they are really more idiomatically correct for hashes like the params.
def show
#search = Search.find(params[:id])
#results = #search.listings
if #results.any? && ['p', 's', 'd'].include?(#results.last[0])
p "#results.pop is either p, s, or d"
#sort_column = #results.pop
#grade = #sort_column.gsub(/[^0-9,.]/, "") unless #results.last[0] == "d"
end
end

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.

Loop through hash in Ruby

My search returns this hash bellow
{"product"=>[{:title=>"It's All About Shoes Barbie Shoe Tree 2010 Hallmark Ornament"}, {:title=>"Iron Lady Bug Key Holder - Hide-A-Key"}]}
here is the loop and the code that generates the hash
id = "B003TM2IDS,B004X75EX4"
ids = id.split(',')
response = []
prod = Hash.new
product = Hash.new
#fetch product title from amazon
for aid in ids do
res = Amazon::Ecs.item_lookup(aid, { :response_group => "ItemAttributes"})
res.items.each do |item|
prod[:title] = item.get("ItemAttributes/Title")
end
# hash
product = {"product" => response.push(prod.dup)}
end
#loop to print the titles - Not working
product.each do |item_prod|
puts item_prod.title
end
I do get the
undefined method `title' for # (NoMethodError)
My question are:
Is the Product hash correct?
Is the loop correct?
I've done this millions of times but some reason I can't see the problem with this
Thanks a lot in advance
Do as below:
product["product"].each do |prod|
puts prod[:title]
end
product["product"].each { |p| puts p[:title] }

Rails Fixnum Error

I have a simple query that Rails seems to be interpreting as a fixnum, but I'm not sure why. My code looks like this:
#user_with_points = Point.select("sum(points) as points, user_id").order("points desc").group("user_id")
#user_with_points.each_with_index do |user_with_point, index|
When I add puts #user_with_points, it shows:
#<Point:0x6360138>
#<Point:0x6322f38>
However, I'm receiving this error this error:
NoMethodError: undefined method 'each' for 75:Fixnum
adding Entire Code
def self.update_overall_rank_and_points
#user_with_points = Point.select("sum(points) as points, user_id").order("points desc").group("user_id")
rank = 0
points = 0
#user_with_points.each_with_index do |user_with_point, index|
#user = User.find(user_with_point.user_id)
if user_with_point.points != points
points = user_with_point.points
rank += 1
end
#user.rank = rank
#user.points = user_with_point.points
#user.save
end
end
Your query is returning a scalar value which the sum of points as an integer. The total of your query happens to be 75, hence the error. Therefore you can't do an each against it since it's not an enumeration.
Try:
#user_with_points = Point.sum(:points, :group => :user_id, :order => 'sum(points)')
#user_with_points.each do |user_id, points|
#...
user = User.find(user_id)
if user.points != points
puts "not equal!"
end
end

Using variables in an array passed to a def - Rails

I'm using the Google charts API to generate a pie chart in my Rails application. However, I'm having a problem passing local variables to the def in the helper. The def takes a 2D array of [label, value] pairs. It doesn't like it when I try to pass a local variable in as the value. These are calculated ahead of time and are in currency format. Putting the variable in quotes or in #{} doesn't work either.
application_helper.rb
def pie_chart(data, options = {})
options[:width] ||= 250
options[:height] ||= 100
options[:colors] = %w(F5DD7E 0DB2AC FC8D4D FC694D FABA32 704948 968144 C08FBC ADD97E)
dt = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-."
options[:divisor] ||= 1
options[:title] ||= "Energy Costs"
while (data.map { |k,v| v }.max / options[:divisor] >= 4096) do
options[:divisor] *= 20
end
opts = {
:cht => "p",
:chd => "e:#{data.map{|k,v|v=v/options[:divisor];dt[v/64..v/64]+dt[v%64..v%64]}}",
:chl => "#{data.map { |k,v| CGI::escape(k)}.join('|')}",
:chs => "#{options[:width]}x#{options[:height]}",
:chco => options[:colors].slice(0, data.length).join(','),
:chf => "bg,s,FFFFFF00",
:chtt => "#{options[:title]}"
}
The def call in the view:
<%= pie_chart( [["Light Cost", #{light_cost}], ["Small Appliance Cost", #{sm_appl_cost}]], :width => 550, :height => 200) %>
How do I pass the local variables?
What's the error you get? You should be able to call it like:
<%=
pie_chart(
[
["Light Cost", light_cost],
["Small Appliance Cost", sm_appl_cost]
],
{ :width => 550, :height => 200 }
)
%>
I added {}'s around the hash just to make it clearer and explicit.
Using #{var} is not the way to do it here because that's for substitution in a string, eg "Here is the value: #{var}".
So that code definitely wants numeric values, but #{} is just going to start a comment unless used inside a string, and you don't want a string there.
And actually, pie_chart as written would appear to take either a 2D array as you say OR a Hash, if the only use of it is with 2-variable .map iterators. So you should be able to use either [["str1", light_cost], ["str2", sm_appl_cost]] or { 'str1'=>light_cost, 'str2'=>sm_appl_cost}.
You do need to make sure that those locals are numeric, though. Try using .to_i or .to_f on them if they aren't.

Resources