DDDD
DDDD

Reputation: 3940

Loop array that has records from two tables

When I try to loop through an array that has records from two tables it gives an error when it can't find the attribute from the other record.

The array has two different types of elements. One starts like: #<Question id: 1,, the other starts with #<Answer id: 1,. How do you get the value of one without it looking for it in the other?

Error:

undefined method `option' for #<Question:0x007fcc2c6610e8>

How would you bypass or loop through this array to show the attribute value when wanted?

The controller:

@pdf = []
@test = Test.find(params[:id])
@test_questions = @test.questions
answers = Answer.all
@all_answers = answers.group_by(&:question_id)
@test_questions.each do |q|
  @pdf << q
  @pdf += @all_answers[q.id]
end

The html where I am trying to only show the content attribute value first, then all the option attribute values

<tbody> 
<% @pdf.each do |test| %>
  <tr>
    <td><%= test.id %></td>
    <% if test.content %>
    <td><%= test.content %></td>
    <% end %>
    <% if test.option %>
    <td><%= test.option %></td>
    <% end %>
  </tr>
<% end %>
</tbody>

This is what the array looks like:

[#<Question id: 1, content: "How did the chicken cross the road?", question_type: "MC", category: "ip_voice", product_id: 8, active: true, created_at: "2014-05-07 17:10:14", updated_at: "2014-05-07 17:10:14", user_id: 1>, #<Answer id: 1, option: "It walked", question_id: 1, correct: false, created_at: "2014-05-07 17:10:14", updated_at: "2014-05-07 17:10:14">, #<Answer id: 2, option: "It was thrown", question_id: 1, correct: true, created_at: "2014-05-07 17:10:14", updated_at: "2014-05-07 17:10:14">, #<Answer id: 3, option: "It got run over and pushed", question_id: 1, correct: false, created_at: "2014-05-07 17:10:14", updated_at: "2014-05-07 17:10:14">, #<Question id: 2, content: "Is this working?", question_type: "TF", category: "ip_voice", product_id: 6, active: true, reated_at: "2014-05-13 16:10:53", updated_at: "2014-05-13 16:10:53", user_id: 1>, #<Answer id: 4, option: "False", question_id: 2, correct: true, created_at: "2014-05-13 16:10:53", updated_at: "2014-05-13 16:10:53">, #<Answer id: 5, option: "True", question_id: 2, correct: false, created_at: "2014-05-13 16:10:53", updated_at: "2014-05-13 16:10:53">]

Attempt for SimonW

<% @options = [] %>
<% @test_questions.each do |q| %>
  <tr>
    <td width="40px;">1.</td>
    <td><%= q.content %></td>
    <% @all_answers.each do |a| %>
      <% @options << a.find(q.id) %>
    <% end %>
  </tr>
  <% @options.each do |o| %>
  <tr>
    <%= o.option %>
  </tr>
  <% end %>
<% end %>

Error:

undefined method `option' for #<Enumerator:0x007fcc4181e678>

Upvotes: 0

Views: 104

Answers (3)

Eki Eqbal
Eki Eqbal

Reputation: 6057

How about iterating through object that response to option method (Duck typing), anyways I agree with SimonW that it's a bad design:

  <% @options.select{|i| i.respond_to?(:option)}.each do |o| %>
    <tr>
      <%= o.option %>
    </tr>
  <% end %>

Upvotes: 0

BroiSatse
BroiSatse

Reputation: 44725

Try instead:

@test = Test.find(params[:id])
@test_questions = @test.questions
answers = Answer.all
@all_answers = answers.group_by(&:question_id)
@pdf = @test_questions.map { |q| [q, @all_answers[q.id]] }

Then in view:

<tbody> 
<% @pdf.each do |question, answers| %>
  <tr>
    <td><%= question.id %></td>
    <td><%= question.content %></td>
    <% answers.each do |answer|
      <td><%= answer.option %></td>
    <% end %>
  </tr>
<% end %>
</tbody>

Update - cleaner solution

Controller:

@test_questions = @test.questions.includes(:answers)

View:

<tbody> 
<% @test_questions.each do |question| %>
  <tr>
    <td><%= question.id %></td>
    <td><%= question.content %></td>
    <% question.answers.each do |answer|
      <td><%= answer.option %></td>
    <% end %>
  </tr>
<% end %>
</tbody>

Upvotes: 2

hp4k
hp4k

Reputation: 356

You can use respond_to? to avoid that error and ensure you don't call the option method on an object which does not know what to do with it:

<% if test.respond_to?(:option) && test.option %>

Upvotes: 1

Related Questions