Assume we have a Severity
class which represents the level of severity which may arise in a system.
This class has a single attribute: a severity string. The levels of severities can range from "info, low, medium, high"
. If we try to sort a collection of severities in ascending order by its level, we get the following result:
1
["info", "low", "high", "medium"]
Likewise for a sort in DESC order based on its levels:
1
["medium", "low", "high", "info"]
The issue here is that the sort order does not represent the inherent meaning of the levels themselves. We want the "high"
level to be at the front of the list when sorting in descending order and "low"
to be at the front in ascending order. Ruby is sorting lexically and does not capture the inherent meaning of the levels.
We can develop a custom comparator class whereby we implement the sort order ourselves.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Severity
attr_accessor :level
def initialize(level)
@level = level
end
end
module SeverityComparator
LEVELS = ["info", "low", "medium", "high"]
module_function
def compare(a, b)
LEVELS.index(a.level) <=> LEVELS.index(b.level)
end
end
The SeverityComparator module holds an array LABELS
whereby we predefine the severity levels in the order we want.
Within the compare
method, we compare the position of the object’s index within the LEVELS
array based on its level attribute. For example:
1
2
3
4
5
6
7
8
9
arr = ["info", "low", "medium", "high"].each_with_object([]){|a, memo|
memo << Severity.new(a)
}
arr.sort{|a,b| SeverityComparator.compare(a,b)}.map(&:level)
# => ["info", "low", "medium", "high"]
arr.sort{|a,b| -SeverityComparator.compare(a,b)}.amp(&:level)
#=> ["high", "medium", "low", "info"]
Supposing that we also discover that the severity levels can also range from "1.0" to "5.0"
. The flexibility of this approach means that we can just add new levels to the LEVELS
array and still use the same comparator:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
module SeverityComparator
# adding new levels
LEVELS = ["1.0", "2.0", "3.0", "4.0", "5.0", "info", "low", "medium", "high"]
# .....
end
arr2 = ["1.0", "2.0", "3.0", "4.0", "5.0"].each_with_object([]){|a, memo|
memo << Severity.new(a)
}
arr2.sort{|a,b| SeverityComparator.compare(a,b)}.map(&:level)
# => ["1.0", "2.0", "3.0", "4.0", "5.0"]
arr2.sort{|a,b| -SeverityComparator.compare(a,b)}.map(&:level)
# => ["5.0", "4.0", "3.0", "2.0", "1.0"]
# sorting still works as before
arr.sort{|a,b| SeverityComparator.compare(a,b)}.map(&:level)
# => ["info", "low", "medium", "high"]
Keep Hacking!!!