0

I have a Ruby hash (originally was a param in rails)

How can I count the number of correctness in each answers_attributes ?

(Why I was doing this is I am trying to create an multiple-choice quiz by rails. One question may have many answers. I was trying to use multi_correct checkbox to decide which question has many correct answers. But that kinda counter-intuitive. So I want the back end to decide by counting the number of correctness in each question)

{
  "utf8" => "✓", "authenticity_token" => "r5xX46JG/GPF6+drEWmMPR+LpOI0jE0Tta/ABQ0rZJJE+UbbEjvNMLP6y2Z9IsWlXq27PR6Odx0EK4NECPjmzQ==", "question_bank" => {
    "name" => "123213", "questions_attributes" => {
      "0" => {
        "content" => "question 1", "multi_correct" => "no", "answers_attributes" => {
          "0" => {
            "content" => "as1", "correctness" => "false"
          }, "1" => {
            "content" => "as2", "correctness" => "false"
          }, "2" => {
            "content" => "as3", "correctness" => "true"
          }, "3" => {
            "content" => "as4", "correctness" => "false"
          }
        }
      }, "1" => {
        "content" => "q2", "multi_correct" => "no", "answers_attributes" => {
          "0" => {
            "content" => "a1", "correctness" => "false"
          }, "1" => {
            "content" => "a2", "correctness" => "false"
          }, "2" => {
            "content" => "a3", "correctness" => "true"
          }, "3" => {
            "content" => "a4", "correctness" => "false"
          }
        }
      }, "2" => {
        "content" => "q3", "multi_correct" => "no", "answers_attributes" => {
          "0" => {
            "content" => "aa1", "correctness" => "false"
          }, "1" => {
            "content" => "aa2", "correctness" => "false"
          }, "2" => {
            "content" => "aa3", "correctness" => "false"
          }, "3" => {
            "content" => "aa4", "correctness" => "true"
          }
        }
      }
    }
  }, "commit" => "Submit"
}
manh.vu
  • 335
  • 1
  • 4
  • 14

2 Answers2

2

I interpreted this as looking for the number of correct answers to each question. If so, you can achieve this using the following:

your_hash['question_bank']['questions_attributes'].values.reduce(Hash.new(0)) do |hash, question| 
  question['answers_attributes'].each do |_k, answer| 
    hash[question['content']] += 1 if answer['correctness'] == 'true'
  end
  hash
end

This gives the result:

# => {"question 1"=>1, "q2"=>1, "q3"=>1}

Basically, what the code does is:

  • iterates through the questions, using reduce to make a hash with a default value of 0 available
  • loops through this question's answers, and adds 1 to the accompanying hash[question_name] (or hash[question['content']] in the example) when the answer is correct
  • returns the accompanying hash

If you're using Rails, which the reference to parameters perhaps hints at, consider using each_with_object rather than reduce; this way you can avoid the trailing hash in the example as it always returns the accumulator.

Hope that helps - let me know if you've any questions.

SRack
  • 11,495
  • 5
  • 47
  • 60
  • Thanks, this does the job well. Although I still don't quite understand how `reduce` work, ruby-doc is a little confusing – manh.vu Aug 15 '18 at 06:12
  • It can be a confusing method. There's a good read [here](https://medium.com/@james.a.hughes/using-the-reduce-method-in-ruby-907f3c18ae1f). In this case, at a very simple level, it simply makes a `hash` available alongside the values of the loop - similar to how, say, `each_with_index` makes the index available. Therefore, you use this hash to store the question names as keys, and add up the correct answers as values for this key. Hope that helps at little, and glad the answer worked :) – SRack Aug 15 '18 at 08:04
1

If you just want to count how many correct answers are in the hash, you can use inject to do that. Here is a way:

# iterate over the questions
hash['question_bank']['questions_attributes'].values.inject(0) do |count, q|
  # iterate over the answers
  count += q['answers_attributes'].values.inject(0) do |a_count, a|
    a_count += a['correctness'] == 'true' ? 1 : 0
  end
end
31piy
  • 23,323
  • 6
  • 47
  • 67
  • 1
    this does return the number of correct answers, but it returns totals of correct. I want it to return the number of correct of each question. Answer above did the job. Thank anyway – manh.vu Aug 15 '18 at 06:08