4

I have to get the Maximum average temperature's country name.

I have used following to get the average temperatures

 this.getTemperatures()
                .stream()
                .collect(Collectors.groupingBy(Temperature::getCountry,
                        Collectors.averagingDouble(Temperature::getAverageTemperature)))

How can I get the maximum or minimum average country name out of this average temperatures list?

Naman
  • 27,789
  • 26
  • 218
  • 353

4 Answers4

4

I don't like this very much because a lot of the code is repeated, but it will work. I can't work out a way to avoid the repetition without making the code worse.

This also iterates all of the map entries twice but given that there are only 195 countries, we're talking about a maximum of 195 extra iterations (if you have measurements for every one), and that's a completely negligible amount for a CPU.

String max = countryToAvgTemp.entrySet().stream()      //stream all entries
    .max(Map.Entry.comparingByValue())                 //get the max by comparing entry value
    .map(Map.Entry::getKey)                            //grab the key   
    .orElseThrow(() -> new RuntimeException("No max")); //e.g. if the list is empty

String min = countryToAvgTemp.entrySet().stream()
    .min(Map.Entry.comparingByValue())
    .map(Map.Entry::getKey)
    .orElseThrow(() -> new RuntimeException("No min"));

If you only want to iterate once, you could write your own collector which returns something like a MinMax<String>. I wrote one but the code wasn't great. Better to keep it simple.

Michael
  • 41,989
  • 11
  • 82
  • 128
  • 2
    `.max(Comparator.comparing(Map.Entry::getValue))` --> ` `.max(Map.Entry.comparingByValue())` – Ousmane D. Jan 10 '19 at 13:06
  • @Aomine Thanks. I'm surprised IntelliJ didn't warn me about that. It's usually pretty good for things like that. – Michael Jan 10 '19 at 14:21
  • Or in a single pass using the `SummaryStatistics` of [this answer](https://stackoverflow.com/a/51378142/2711488): `SummaryStatistics> s = countryToAvgTemp.entrySet().stream() .collect(SummaryStatistics.statistics(Map.Entry.comparingByValue())); String min = s.getMin().getKey(), max = s.getMax().getKey();` – Holger Jan 10 '19 at 14:28
  • @Holger Sure but I wouldn't recommend taking an entire class from Stack Overflow without any tests or anything. – Michael Jan 10 '19 at 14:35
  • Well, a lot of people use entire class libraries without any tests, just because it is a library with a name. I can assert you that I have tests for that class in my code base and it’s not that hard for any developer to write tests for it. In the end, when implementing it on their own instead of taking it from Stackoverflow, they had to write the same tests. – Holger Jan 10 '19 at 14:45
1

Use

Collections.min(temperatureMap.entrySet(), Comparator.comparingInt(Map.Entry::getValue)).getValue()

and

Collections.max(temperatureMap.entrySet(),  Comparator.comparingInt(Map.Entry::getValue)).getValue()
Leonardo
  • 1,263
  • 7
  • 20
  • 51
1

If you want get the maximum or minimum average country name,you can sort the temperatures list and then get the first and last element。But your work do not need sorted list,it is not a good method,@Michael's method is very good for you.

       List<Temperature> temperatures = Arrays.asList(
                new Temperature("a",10),
                new Temperature("b",11),
                new Temperature("c",12),
                new Temperature("d",13),
                new Temperature("e",14),
                new Temperature("f",15),
                new Temperature("g",16),
                new Temperature("h",17));

        temperatures = temperatures.stream().sorted(new Comparator<Temperature>() {
            @Override
            public int compare(Temperature o1, Temperature o2) {
                return (int) (o1.getAverageTemperature() - o2.getAverageTemperature());
            }
        }).collect(Collectors.toList());

        String min = temperatures.get(0).getCountry();
        String max = temperatures.get(temperatures.size()-1).getCountry();
TongChen
  • 1,414
  • 1
  • 11
  • 21
1

You can try DoubleSummaryStatistics:

this.getTemperatures()
            .stream()
            .collect(Collectors.groupingBy(Temperature::getCountry,
                    Collectors.summarizingDouble(Temperature::getAverageTemperature)));

this will return a map:

Map<Country, DoubleSummaryStatistics>

So with DoubleSummaryStatistics, you can get count, sum, min, max, average of each country

Twister
  • 161
  • 4