Reputation: 33
I'm currently writing an automated team formation program in Ruby, which makes heterogeneous teams based on the students' skill. The skill is derived from a grade of the student from the previous year(i.e. an integer in the range [0;100]). I have implemented a default version of the grade to skill conversion method, where the grade is transformed into skill according to the following thresholds
[0;39] => 1
[40;49] = > 2
[50;59] = > 3
[60;69] = > 4
[70;79] = > 5
[80;100] = > 6
And that is implemented via case/when statement However, the program will be designed to the used by the teacher and I want to provide the teacher with the capability to provide his own interpretation of how to measure students' skill(for example he may want to have lower or higher number of skill groups or different threshold for the given skill groups). My idea is that he will be able to pass them as named argument in the form of hash, with 2-valued array as a key(the grade thresholds) and an integer as value(the skill), however I'm struggling to think of a neat way to implement it. Is there any possibility of implementing variable number of when statements, where each one is responsible for a given skill(like putting them in some kind of loop?). Or can you think of any other way this can be solved? Thanks a lot in advance for your help!
Upvotes: 2
Views: 52
Reputation: 198324
Using bsearch
is more performant than find
, as long as you can guarantee an ordered array – O(log n)
vs find
's O(n)
– without any extra expenditure of memory (except stack frames, hehe). Also, since your ranges are (presumably) meant to be consecutive, without any holes between them, you can just specify threshold values.
So, if your indices are always "1..N", this works:
def grade(points, scale)
scale.bsearch_index { |x| points < x }
end
scale = [0, 40, 50, 60, 70, 80, 101]
grade(39, scale)
# => 1
grade(40, scale)
# => 2
If not, then use this:
def grade(points, scale)
scale.to_a.bsearch { |upto, _| points < upto }.last
end
scale = {40 => 1, 50 => 2, 60 => 3, 70 => 4, 80 => 5, 101 => 6}
grade(39, scale)
# => 1
grade(40, scale)
# => 2
Upvotes: 2
Reputation: 114178
Given a structure like this:
values = {
0..39 => 1,
40..49 => 2,
50..59 => 3,
60..69 => 4,
70..79 => 5,
80..100 => 6
}
You can use find
to get the first entry matching a certain condition: (assuming the ranges don't overlap and that there are no "holes" – you might want to validate the provided data first)
grade = 52
range, skill = values.find { |r, v| r.cover?(grade) }
range #=> 50..59
skill #=> 3
Upvotes: 3