Home > Blockchain >  What is The Proper Way to Loop Through a set of Records and Match One of the Attribute's Values
What is The Proper Way to Loop Through a set of Records and Match One of the Attribute's Values

Time:01-25

I have a Model called Report, and this model contains a column report_type which stores a string value. A Report is associated to a User. Each type of Report is worth a point value. I have a model concern that I want to use to loop through a User's reports and calculate that users points. I have a constant in the concern that knows the report_type point mapping, but am struggling to realize the most efficient way to loop through a user's reports and calculate their points. This is what I have thus far. The points method at the bottom is where I am unsure of how to proceed. I suspect there is a simple method I can use instead of looping through the object verbosely each time points is called.

I am also open to architecting this differently if there is an overall better approach.

module PointsConcern
  extend ActiveSupport::Concern

  included do
    before_action :set_current_user
  end

  REP_TYPES = {
    PUSH_UP => 1,
    AIR_SQUAT = >1,
    BAR_DIP => 1,
    BENCH_DIP => 0.5,
    CHIN_UP => 1.5,
    PULL_UP => 2,
    HANDSTAND_PUSH_UP => 3,
    BACK_EXTENSIONS => 1,
    MOUNTAIN_CLIMBER => 0.5,
    BURPEE => 1.5
  }

  def set_current_user
    if session[:user_id]
      @current_user = User.find(session[:user_id])
    end
  end

  def reports
    @reports = Reports.where(user_id: @current_user.user_id)
  end

  def points
    points = 0
    @reports.each do |r|
      r.report_type # What do?
    end
  end
end

CodePudding user response:

I'd start by making it so that a report itself can tell how many points it's worth. You can do this either using single-table inheritance, or by adding a mapping inside the base Report class. See example below:

class Report
  def points
    case report_type
    when "push_up" then 1
    when "air_squat" then 1
    when "bar_dip" then 1
    when "bench_dip" then 0.5
    when "chin_up" then 1.5
    when "pull_up" then 2
    when "handstand_push_up" then 3
    when "back_extensions" then 1,
    when "mountain_climber" then 0.5
    when "burpee" then 1.5
  end
end

You could also use a hash where the report type is the key and the points the value, with the same result.

Now, summing the points is trivial:

Report.where(user_id: @current_user.user_id).sum(&:points)

CodePudding user response:

I recommend you to redesign it in the following way:

  • create 2 enums in the Report model report_type as string & report_score as float, enums should look like this:
enum report_type: {
  PUSH_UP: 'PUSH_UP',
  AIR_SQUAT: 'AIR_SQUAT',
  BAR_DIP: 'BAR_DIP',
  ...
}

enum report_score: {
  PUSH_UP_SCORE: 1,
  AIR_SQUAT_SCORE: 1,
  BAR_DIP_SCORE: 1,
  ...
}

In this case, to create a new record you need to do the following

Report.create(report_type: params[:report_type], report_score: "#{params[:report_type]}_SCORE")

And to calculate the score of user's reports, you need to do something like that:

user.reports.sum(:report_score)
  •  Tags:  
  • Related