2

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!

5
  • 2
    Hint: find on an array structure with [ range, value ] as pairs. Commented Apr 3, 2020 at 5:56
  • The brute-force lazy way is to make a hash with 100 keys and just populate all the values. Commented Apr 3, 2020 at 5:56
  • The problem is that this is my masters final year project and I'm looking for the smartest possible way to do it, otherwise, yeah, this is a possible solution Commented Apr 3, 2020 at 6:01
  • 2
    "Smartest" as in "most efficient" depends on a variety of factors. A linear scan will be O(n) while a hash or an array is O(1) which is better, but takes more time to construct and memory to keep around. Commented Apr 3, 2020 at 6:16
  • 1
    Absolutely true. However for that case, the whole algorithm has quite big time and space complexity and this will be a negligible portion of the run time, regardless of how efficient it is. Just didn't want to go with a brute a brute-forcing approach, but go with something at least looking neat. Thanks anyway! Commented Apr 3, 2020 at 6:21

2 Answers 2

3

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 
Sign up to request clarification or add additional context in comments.

2 Comments

Note that traversing a hash that way isn't optimal. If you only have 100 entries in total, you might want to convert it to a "flattened" hash containing all 100 grade => skill pairs. Something like values.flat_map { |r, v| r.to_a.map { |k| [k, v] } }.to_h should work.
Yeah, I guess I'll do it that way, the hash will have 250-300 entries
2

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 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.