Search code examples
rubyruby-hash

How to count number of appearance of a specific value in Ruby hash?


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"
}

Solution

  • 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.