Natalie_eds
Natalie_eds

Reputation: 71

Put a sum as value in a hashmap

My two lists list2 and filetemp are of the same lengths and contain dates and temperatures. A few of the elements in the lists are shown below:

[1946-01-12, 1946-01-12, 1946-01-12, 1946-01-13, 1946-01-13, 1946-01-13, 1946-01-14, 1946-01-14, 1946-01-14]
[-1.3, 0.3, -2.8, -6.2, -4.7, -4.3, -1.5, -0.2, -0.4]

I want to put them in a hashmap with dates as key and temperature as value. Since I have several temperature values corresponding to each date, I want to sum the temperature values for each day (or if possible calculate average temperature for each day) so that each date in the map corresponds to the average temperature of that date. My problem now is that my map looks like:

1946-01-12: -2.8
1946-01-13: -4.3
1946-01-14: -0.4

so instead of summing for example -1.5+(-0.2)+(-0.4) for date 1946-01-14 it just returns the last temperature of that date. Does anyone know how to fix it?

public Map<LocalDate, Double> avarageTemperatures(LocalDate dateFrom, LocalDate dateTo) {
    List<LocalDate> list2 = new ArrayList<>();
    for (Weather weather : weatherData) {
        list2.add(weather.getDateTime());
    }

    Map<LocalDate, Double> map = new HashMap<>();
    List<Double> filetemp = new ArrayList<>();
    for (Weather weather : weatherData) {
        filetemp.add(weather.getTemperature());
    }
    Double val= 0.0;
    for (int i=1; i<list2.size(); i++) {
        if(list2.get(i)==list2.get(i-1)) {
            val+= filetemp.get(i);
            map.put(list2.get(i), val);
        } else {
            val=filetemp.get(i);
            map.put(list2.get(i), val);
        }
    }

    Set<Map.Entry<LocalDate, Double>> entrySet = map.entrySet();
    for(Map.Entry<LocalDate,Double> entry : entrySet) {
        if(!entry.getKey().isAfter(dateTo) && !entry.getKey().isBefore(dateFrom)) {
            System.out.println(entry.getKey()+": "+(entry.getValue()));
        }
    }

    return map;
}

Upvotes: 1

Views: 172

Answers (3)

WJS
WJS

Reputation: 40034

Well, here is one way using streams and creating a map of maps. The inner map simply holds the sum and average temperatures. A List could also have been used but the map allows symbolic indexing to help keep things straight.

It works as follows:

  • Use the Collectors.teeing method to create two streams. The first does a frequency count of dates. The second, adds the temps. Both end up in maps with the dates as keys.
  • Then compute the average temp by dividing the sum by the frequency count of dates. This allows the number of dates for each sample to vary in number and still allow an accurate average.
  • Both the sum of temperatures and the average temperature are stored as a map.
  • A TreeMap is used for the outer map to sort the keys in their natural order.

    List<String> dates =
            List.of("1946-01-12", "1946-01-12", "1946-01-12",
                    "1946-01-13", "1946-01-13", "1946-01-13",
                    "1946-01-14", "1946-01-14", "1946-01-14");

    List<Double> temps = List.of(-1.3, 0.3, -2.8, -6.2, -4.7,
            -4.3, -1.5, -0.2, -0.4);

    Map<String,Map<String, Double>> data = IntStream
            .range(0,
                    dates.size())
            .boxed()
            .collect(Collectors.teeing(
                    Collectors.groupingBy(i -> dates.get(i),
                            Collectors.counting()),
                    Collectors.groupingBy(i -> dates.get(i),
                            Collectors.summingDouble(
                                    i -> temps.get(i))),
                    (counts, temp) -> temp.keySet().stream()
                            .collect(Collectors.toMap(k->k,
                                    k->Map.of("SUM", temp.get(k), "AVG",temp.get(k)
                                            / counts.get(k)), (m,n)->n, TreeMap::new))));



    for (String k : data.keySet())  {
        System.out.printf("For %s - sum of temperatures was %6.3f%n",k,data.get(k).get("SUM"));
        System.out.printf("For %s - average temp was %6.3f%n",k,data.get(k).get("AVG"));
        System.out.println();
     }

It prints the following:

For 1946-01-12 - sum of temperatures was -3.800
For 1946-01-12 - average temp was -1.267

For 1946-01-13 - sum of temperatures was -15.200
For 1946-01-13 - average temp was -5.067

For 1946-01-14 - sum of temperatures was -2.100
For 1946-01-14 - average temp was -0.700


Upvotes: 2

Nonika
Nonika

Reputation: 2560

Hope this helps:

    List<String> dates = Arrays.asList("1946-01-12", "1946-01-12", "1946-01-12", "1946-01-13", "1946-01-13", "1946-01-13", "1946-01-14", "1946-01-14", "1946-01-14");
    List<Double> values = Arrays.asList(-1.3, 0.3, -2.8, -6.2, -4.7, -4.3, -1.5, -0.2, -0.4);

    Map<String, DoubleSummaryStatistics> res = IntStream
            .range(0, dates.size())
            .boxed()
            .map(index -> new Pair<>(dates.get(index), values.get(index)))
            .collect(groupingBy(Pair::getKey,
                    summarizingDouble(Pair::getValue)));

    System.out.println(res);

and the result:

{1946-01-14=DoubleSummaryStatistics{count=3, sum=-2.100000, min=-1.500000, average=-0.700000, max=-0.200000}, 
1946-01-12=DoubleSummaryStatistics{count=3, sum=-3.800000, min=-2.800000, average=-1.266667, max=0.300000}, 
1946-01-13=DoubleSummaryStatistics{count=3, sum=-15.200000, min=-6.200000, average=-5.066667, max=-4.300000}}

Upvotes: 0

Jason
Jason

Reputation: 5246

This is one way you can do it. To get a List of Weather objects like you have we create a List of Weather objects using the array of String and array of Double values. From that List we simply collect using Collectors#groupingBy which allows us to determine the key we want to group by, which in this case is LocalDate, and the Collector used to group all the relevant key values together, which in this case is Collectors#summingDouble since we want to sum all double values.

        String[] dates = {
                "1946-01-12", "1946-01-12", "1946-01-12", "1946-01-13", "1946-01-13", "1946-01-13",
                "1946-01-14", "1946-01-14", "1946-01-14"
        };

        double[] temperatures = {-1.3, 0.3, -2.8, -6.2, -4.7, -4.3, -1.5, -0.2, -0.4};

        class Weather {

            private final LocalDate date;

            private final double temperature;

            Weather(LocalDate date, double temperature) {
                this.date = date;
                this.temperature = temperature;
            }

            public LocalDate getDate() {
                return date;
            }

            public double getTemperature() {
                return temperature;
            }
        }

        List<Weather> weather = IntStream.range(0, dates.length)
                .mapToObj(index -> new Weather(LocalDate.parse(dates[index]), temperatures[index]))
                .collect(Collectors.toList());        

       Map<LocalDate, Double> temperaturesSum = weather.stream()
                .collect(Collectors.groupingBy(Weather::getDate,
                        Collectors.summingDouble(Weather::getTemperature)));

        Map<LocalDate, Double> temperaturesAverage = weather.stream()
                .collect(Collectors.groupingBy(Weather::getDate,
                        Collectors.averagingDouble(Weather::getTemperature)));

        System.out.println("sum: " + temperaturesSum);
        System.out.println("average: " + temperaturesAverage);

Output

sum: {1946-01-12=-3.8, 1946-01-13=-15.2, 1946-01-14=-2.1}
average: {1946-01-12=-1.2666666666666666, 1946-01-13=-5.066666666666666, 1946-01-14=-0.7000000000000001}

Upvotes: 1

Related Questions