0

I have objects like this:

class DailyReport{
LocalDate day;
Long criticalErrors;
Long browserErrors;
Long OtherErrors;
}

Now I get list of this objects and want to create summaryobject i.e

class Summary{
  LocalDate startDate;
LocalDate endDate;
Long sumOfCritical;
Long sumOfBrowser;
Long sumOfOthers
}

I created sth like this:

List<DailyReport> dailyReports=repository.findAll();
Summary summary = new Summary(
 dailyReports.get(0).getDay;
 dailyReports.get(dailyReports.size() - 1).getDay;
summary.stream().mapToLong(DailyReport::getCriticalErrors).sum(),
summary.stream().mapToLong(DailyReport::getBrowserErrors).sum(),
summary.stream().mapToLong(DailyReport::getOtherErrors).sum(),
}

But for me it looks inefficient - is there any better way to do this?

Adam
  • 151
  • 1
  • 2
  • 14
  • Why use Long, not long? – GhostCat Aug 16 '18 at 12:03
  • 1
    What looks inefficient to you (apart from the unnecessary boxing/unboxing operations)? – assylias Aug 16 '18 at 12:03
  • 4
    And please fix the source example. Especially the last part is mixing up , with ; and uses ( ... but then closes with }. Seriously: dont dump stuff on us that doesn't even compile! – GhostCat Aug 16 '18 at 12:03
  • 1
    I assume you are talking about the 3 sums when you say "inefficient". Did you check [this](https://stackoverflow.com/questions/40567935/java8-stream-sum-multiple)? – BackSlash Aug 16 '18 at 12:04
  • `It looks inefficient` => I think it is not. But it looks very readable, which is fine. – Arnaud Denoyelle Aug 16 '18 at 12:25

2 Answers2

2

You can reduce a stream by implementing the logic of "adding" a report to a summary object, and the logic of combining two summary objects.

Here's what that looks like:

//Adds a report to a summary
BiFunction<Summary, DailyReport, Summary> reportMerger = (summ, rep1) -> {
    Summary summary = new Summary();

    summary.setStartDate(rep1.getDay().isAfter(summ.getStartDate()) ? 
                rep1.getDay() : summ.getStartDate());
    summary.setEndDate(rep1.getDay().isAfter(summ.getEndDate()) ? 
                summ.getEndDate() : rep1.getDay());
    summary.setSumOfCritical(rep1.getCriticalErrors() 
                + summ.getSumOfCritical());
    summary.setSumOfBrowser(rep1.getBrowserErrors() 
                + summ.getSumOfBrowser());
    summary.setSumOfOthers(rep1.getOtherErrors() 
                + summ.getSumOfOthers());

    return summary;
};

//combines 2 summary objects
BinaryOperator<Summary> summaryMerger = (s1, s2) -> {
    Summary summ = new Summary();

    summ.setStartDate(s1.getStartDate().isBefore(s2.getStartDate()) ? 
                s1.getStartDate() : s2.getStartDate());
    summ.setEndDate(s1.getEndDate().isBefore(s2.getEndDate()) ? 
                s2.getEndDate() : s1.getEndDate());

    summ.setSumOfCritical(s1.getSumOfCritical() 
                + s2.getSumOfCritical());
    summ.setSumOfBrowser(s1.getSumOfBrowser() 
                + s2.getSumOfBrowser());
    summ.setSumOfOthers(s1.getSumOfOthers() 
                + s2.getSumOfOthers());

    return summ;
};

And then reduce a stream of reports into a single summary

Summary summary = dailyReports.stream()
     .reduce(new Summary(), reportMerger, summaryMerger);

Note: you need to add null checks for the null fields of new Summary() in the comparisons above (to avoid NPEs).


An alternative way to collect (thanks to Eugene's comments) that removes the need for new objects:

Summary summaryTwo = dailyReports.stream().collect(Collector.of(Summary::new, 
    (summ, rep) -> {
        //add rep to summ
    }, (summary1, summary2) -> {
        //merge summary2 into summary1
        return summary1;
    }));
ernest_k
  • 44,416
  • 5
  • 53
  • 99
  • 1
    I *think* you can do the same to a *mutable* reduction, via `Collector.of` – Eugene Aug 16 '18 at 12:26
  • @Eugene Almost... except for the `startDate` and `endDate` part, which won't have a place when using a `BiConsumer` – ernest_k Aug 16 '18 at 12:34
  • 1
    it looks like you are coding like a *mutable reduction*, but using `reduce`, in each step for `reduce` you must return a new instance – Eugene Aug 16 '18 at 12:39
  • @Eugene 100%. Edited last function – ernest_k Aug 16 '18 at 12:43
  • and now you get to my first point, you can do the same thing, without the extra objects, if you would use `Collector.of`, right? – Eugene Aug 16 '18 at 12:44
  • Right. I'm sure I read that too quickly (thought `stream.collect`, not `Collector.of`). Will add – ernest_k Aug 16 '18 at 12:51
  • `stream.collect(Collector.of(...))` since it looks like you might need a finisher (or not, have not looked to close, I admit) – Eugene Aug 16 '18 at 12:52
1

You can loop on dailyReports and calculate values like

LocalDate startDate =  dailyReports.get(0).getDay;
LocalDate endDate =  dailyReports.get(dailyReports.size() - 1).getDay;

Long sumOfCritical=0L;
Long sumOfBrowser=0L;
Long sumOfOthers=0L;

dailyReports.stream().foreach(o->{ 
sumOfCritical += o.getCriticalErrors();
sumOfBrowser += o.getBrowserErrors();
sumOfOthers += o.getOtherErrors();
})

Summary summary = new Summary(startDate, endDate,sumOfCritical, sumOfBrowser, sumOfOthers)
Manasi
  • 765
  • 6
  • 17