Search code examples
javacollectionshashmapdoublethreshold

Classify a list of value through a list of thresholds


I have a map of <String,Double> considered as thresholds to classify a list of Doubles

HashMap<String, Double> map = new Hashmap<>();
map.put(A/B,0.7);
map.put(B/C,0.4);
map.put(C/D,0.3);
map.put(D/E,0.1);

The Doubles of the hash is used as threshold to classify a list doubles given, so I want to transform this map into a list of classes A, B, C, D

Class A : from 0.7
Class B : from 0.4 to 0.7
Class C : from 0.3 to 0.4
Class D : from 0.1 to 0.3
Class E : less than 0.1

Do you have any idea on how to perform that as a method StringClassifyByValue(HashMap<String, Double> map, Double value){} returning a String of the class correspondent of the value given as parameter?

ِExample :

this.StringClassifyByValue(map,0.5) have to return B.


Solution

  • Some thoughts: first of all, your data structure is not really helping with the problem you want to solve. But that is exactly what data structures exist for: to give you a helpful abstraction that allows you to efficiently solve your "most important" problem.

    Coming from there, I would suggest that you start by creating classes that better fit your problem statement. You could start with something like

    public class Interval {
    
       private final double lowerBoundary;
       private final double upperBoundary;
    
       public Interval(double lowerBoundary, upperBoundary) {
         this.lowerBoundary = ...
       } 
    
       public boolean contains(double value) {
         return (value >= lowerBoundary) && (value <=upperBoundary);
       }
    

    And instead of keeping of using a Map<String, Double> you rather have something like List<Pair<Classification, Interval>> where:

    • Pair could be a class that simply holds two values that belong together (instead of using a "generic" pair class, you could also create your own custom class that combines a Classification with an Interval).
    • Classification represents a "class", like A, B, C, in your example. The point is: be careful about using raw String objects for such purposes. Maybe a simple string is fine for now, but maybe you have to further enhance your logic later on - to then find that "hm, now a simple string doesn't do any more, but now I have to update a ton of places to change that".

    And of course: ideally, you would sort the above list by "intervals". Then finding the Classification for a specific double value is super simple:

     for (Pair<Classification, Interval> combo : listOfPairs) {
       if (combo.getInterval().contains(value)) {
         return combo.getClassification(); // yeeha found one
       }
     }
     return "nothing found" ... or throw some kind of exception
    

    Long story short: I can't tell you how to best transform your existing map into the above list of pair objects - because I don't know the big picture. It might be possible to simply not create that map initially, and directly build such a list of objects.

    And for the record: if that map exists as map because there are other, more important requirements ... then you have to carefully balance if you really want to keep using map+list (means "double book-keeping") or if you put all of the above logic into some service that turns your Map into a List, does the lookup and then throws away this "other" representation of your data.