Virge Assault
Virge Assault

Reputation: 1396

Nil errors in creating a hash in Ruby on Rails

I'm running into several syntax and nil issues when trying to create this hash idea_benefit_count.

@idea = @domain.ideas
@idea_evaluations = Array.new()
@idea.each do |idea|
  @idea_evaluations << idea.evaluations
end

@idea_benefit = []
if !@idea_evaluations.nil?
  @idea_evaluations.each do |eval|
    @idea_benefit << eval.benefit
  end
  @idea_benefit_count = Hash.new(0)
  @idea_benefit.flatten.each { |idea_benefit| @idea_benefit_count[idea_benefit] += 1 }
end

An idea has_many domains, so @idea should be all of the ideas that has a given @domain.

@idea_evaluations is an array of all evaluations that belong_to a specific idea.

@idea_benefit is an array of arrays that holds all of each evaluation's benefit entries. example output: [["happier_customers"], ["happier_employees"], ["happier_employees", "decreased_costs"], ["happier_employees"]]

idea_benefit_count reads idea_benefit and counts how many of each type of benefit an idea has. The output of idea_benefit_count based on the above idea_benefit array is {"happier_customers"=>1, "decreased_costs"=>1, "happier_employees"=>3}

(Hopefully this context covers everything)

My current error is undefined method 'benefit' on the line @idea_benefit << eval.benefit. Benefit is an attribute of evaluation, so it should output something like ["happier_customers"].

I realize this code is a bit of a mess, so any suggestions would be super helpful on getting the entire thing working. A big problem is checking to see if there are evaluations belonging to an idea, because if this comes back nil it throws an error. Tried to fix that with the if !@idea_evaluations.nil? check, but haven't been able to test it yet because of the undefined method 'benefit' error.

EDIT

@idea_evaluations = Array.new()
@idea.each do |idea|
  @idea_evaluations << idea.evaluations
end
@idea_evaluations.flatten!

@idea_benefit = []
unless !@idea_evaluations.nil?
  @idea_evaluations.each do |eval|
    @idea_benefit << eval.benefit
  end
  @idea_benefit_count = Hash.new(0)
  @idea_benefit.flatten.each { |idea_benefit| @idea_benefit_count[idea_benefit] += 1 }
end

Upvotes: 1

Views: 138

Answers (3)

user12341234
user12341234

Reputation: 7203

I believe this entire snippet can be replaced by:

@idea_benefit_count  = 
  @idea.flat_map(&:evaluations)
       .flat_map(&:benefit)
       .each_with_object(Hash.new{0}) {|k, h| h[k] += 1}

Upvotes: 0

fylooi
fylooi

Reputation: 3870

Use map to build up the array (or an ActiveRecord scope for better efficiency).

@idea_evaluations = @domain.ideas.map(&:evaluations)

# Remove nil values before moving on to the next chunk.
@idea_evaluations.reject! { |i| i.nil? }
@idea_benefits = @idea_evaluations.map(&:benefit)

If you go with the scope approach, it would look similar to the following:

# in Benefit.rb 
scope for_domain ->(domain_id){ joins(:evaluation => :idea).where(idea: {domain_id: domain_id}) }

# called like this 
@idea_benefits = Benefit.for_domain(@domain.id)

inject is a useful tool to summarize information.

@idea_benefits .inject({}) do |eval, result|
  result[eval] += 1 
  result
end

Upvotes: 0

Mircea
Mircea

Reputation: 10566

the problem that I see if I understand what you are trying to do is the way you are dumping idea.evaluations into @idea_evaluations.
@idea_evaluations is going to be an array of arrays of evaluations not an array of evaluations as you think.
e.g. [[eval1,eval2],[eval3,eval4,eval5]] instead of [eval1, eval2, eval3, eval4, eval5]

when you try to access the benefit, it attempts to look for a benefit method for the array which does not exist.

try altering

@idea.each do |idea|
  @idea_evaluations << idea.evaluations
end

to be

@idea.each do |idea|
  @idea_evaluations << idea.evaluations
end
@idea_evaluations.flatten!

also in ruby you usually don't do

if !condition 

you normally do

unless condition

Upvotes: 1

Related Questions