Skip to content

Instantly share code, notes, and snippets.

@kamchy
Last active February 8, 2021 13:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kamchy/5b2b7f938b779391c8a401c842e98793 to your computer and use it in GitHub Desktop.
Save kamchy/5b2b7f938b779391c8a401c842e98793 to your computer and use it in GitHub Desktop.
Example usage of java 8 features: lambdas, streams and java.time
package com.kamilachyla;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.time.Duration;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.StringJoiner;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class DateTimeSeries {
private static final Function<String, LocalDate> toLocalDate =
(String s) -> LocalDate.parse(s, DateTimeFormatter.ISO_LOCAL_DATE);
private static final Consumer<RunningAverage> averagePeriodPrinter =
(RunningAverage avg) -> System.out.print(String.format("Average period length: %,.2f%n", avg.average()));
private static final Consumer<RunningAverage> nextDatePredictor =
(RunningAverage ra) -> System.out.println(
String.format("Predicted next date: %s", ra.previous.plusDays(Math.round(ra.average()))));
public static void main(String[] args) {
try (BufferedReader is = new BufferedReader(new InputStreamReader(System.in))) {
Stream<String> lines = is.lines().filter(s -> !s.isEmpty());
List<LocalDate> dates = lines.map(toLocalDate).collect(Collectors.toList());
RunningAverage average = calculateAveragePeriod(dates);
averagePeriodPrinter.andThen(nextDatePredictor).accept(average);
} catch (IOException e) {
e.printStackTrace();
}
}
private static RunningAverage calculateAveragePeriod(List<LocalDate> dates) {
return dates.stream().reduce(RunningAverage.initial(), RunningAverage::updated, RunningAverage::plus);
}
/**
* Represents running average of durations between dates.
* Provides average() method for calculating average duration in days
* for all accumulated durations. It should be called after two calls to update()
* (otherwise it throws if called before *second* call to update).
*
* First call to update() sets up *previous* date field.
* Subsequent calls update periodSum (sum of durations) and count (of durations encountered).
* */
private static final class RunningAverage {
private final LocalDate previous;
private final double periodSum;
private final int count;
static RunningAverage initial() {
return new RunningAverage(null, 0, 0);
}
private RunningAverage(LocalDate initial, double periodSum, int count) {
this.previous = initial;
this.periodSum = periodSum;
this.count = count;
}
@Override
public String toString() {
return new StringJoiner(", ", RunningAverage.class.getSimpleName() + "[", "]")
.add("previous=" + previous)
.add("periodSum=" + periodSum)
.add("count=" + count)
.add("avg=" + (count > 0 ? String.format("%,.2f", average()) : "unknown"))
.toString();
}
RunningAverage updated(LocalDate next) {
double daysFromPrevToNext = previous == null ? 0 : Duration.between(previous.atStartOfDay(), next.atStartOfDay()).toDays();
return new RunningAverage(
next,
periodSum + daysFromPrevToNext,
count + (previous != null ? 1 : 0));
}
double average() {
if (count <= 0) {
throw new IllegalArgumentException("Count should be greater than 0");
}
return periodSum / count;
}
RunningAverage plus(RunningAverage avg2) {
return new RunningAverage(avg2.previous, periodSum + avg2.periodSum, count + avg2.count);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment