Student.java

```
public class Student{
    /*
    Metrics examples (out of 100):
    mathScore:98
    scienceScore:99
    englishScore:92
    */
    private Map<String, Double> metrics = new HashMap<>();

}

```

Statistics

```
public enum Statistic {

     AVERAGE,
     MIN,
     MAX
}

```

Result

```
public class Result {
  private Statistic statistic;
  private double value;
  private String metric;
}

```

What's the most efficient way to build an engine that can perform requested statical analysis based on the requested metrics on a list of students

Here's an example:

Say I have a list of Students. 

```
List<Student> students = [john, json, elliot, sarah, callie, matt, leigh]; //Assume each entry is an object 

```

I also have list of metrics I'm interested in 

```
List<String> metrics  =["mathScore", "scienceScore"]; //Just an example. I can add additional metrics to this list or remove them.

```

And the statistics I want to perform

```
List<Statistic> stats = [MIN, MAX]; //Just an example. I can request additional operations if necessary.

```

Here's the signature of the method that needs to be efficiently built

```
public List<Resut> calculate( List<String> requestedMetrics, List<Statistic> requestedStatistics, List<Student> students){

}

```

Here are my initial thoughts

1. Convert requestedMetrics and requestedStatistics to sets to
eliminate duplicates.
2. Iterate through each metric. For each metric,
iterate through each statistic and calculate it. Is there a better
way? And how does one go about breaking down the implementation in
to smaller functions etc. for a cleaner solution?
3. What about creating a cache (map) so that we don't need to reprocess 
everything again and again?

Here's my current implementation

```
@Component
public class StatisticalAnalysis {

    @Override
    public List<Result> calculate(List<Student> students, List<String> metrics, List<Statistic> stats) {

        return analyze(new HashSet<>(students), new HashSet<>(metrics), new HashSet<>(stats));
    }

    public List<Result> analyze(HashSet<Student> students, HashSet<String> metrics, HashSet<Statistic> stats) {

        List<Result> calculate = new ArrayList<>(metrics.size());

        for (String metric : metrics) {
            for (Statistic stat : stats) {
               results.add(createResult(students, metric, stat ));
            }
        }

        return results;
    }

    private Result createResult(HashSet<Student> students, String metric, Statistic stat) {

       return new Result(metric, stat, calcStatValue(students, metric, stat));

    }

    private double calcStatValue(HashSet<Student> students, String metric, Statistic stat) {

        List<Double> values = new ArrayList<Double>(students.size());

        for(Student measurement: students){
            Double value = measurement.getMetric(metric);
            if(value!=null)
                values.add(value);
        }

        return performStatOperation(stat, values);

    }

    private double performStatOperation(Statistic stat, List<Double> values) {
        switch (stat) {
            case MIN:
                return Collections.min(values);
            case MAX:
                return Collections.max(values);
            case AVERAGE:
                return values.stream().mapToDouble(val -> val).average().orElse(0.0);
            default:
                throw new UnsupportedOperationException(String.format("Calculation of Statistic %s is currently unsupported", stat));
        }
    }

}

```