diff --git a/src/main/java/no/nav/fpsak/tidsserie/StandardCombinators.java b/src/main/java/no/nav/fpsak/tidsserie/StandardCombinators.java index 85673f9..ca90a7e 100644 --- a/src/main/java/no/nav/fpsak/tidsserie/StandardCombinators.java +++ b/src/main/java/no/nav/fpsak/tidsserie/StandardCombinators.java @@ -6,9 +6,13 @@ import java.util.Arrays; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; import no.nav.fpsak.tidsserie.LocalDateTimeline.JoinStyle; @@ -57,8 +61,7 @@ public static LocalDateSegment> allValues(LocalDateInterval dateInte * Basic combinator som alltid returnerer Boolean.TRUE for angitt interval. Greit å bruke når verdi ikke betyr * noe, kun intervaller. Merk hvilket intervall som benyttes avhenger av {@link JoinStyle} og "includeGaps". Alle som passer får True. */ - public static LocalDateSegment alwaysTrueForMatch( - LocalDateInterval dateInterval, + public static LocalDateSegment alwaysTrueForMatch(LocalDateInterval dateInterval, @SuppressWarnings("unused") LocalDateSegment lhs, // NOSONAR @SuppressWarnings("unused") LocalDateSegment rhs // NOSONAR ) { @@ -137,6 +140,94 @@ public static LocalDateSegment concat(LocalDateInterval dateInterval, return new LocalDateSegment<>(dateInterval, (lv == null ? "" : lv) + (rv == null ? "" : rv)); } + /** + * Basic combinator som tar minste verdi, evt lhs dersom like + */ + public static > LocalDateSegment min(LocalDateInterval dateInterval, + LocalDateSegment lhs, + LocalDateSegment rhs) { + if (lhs != null && rhs != null) { + var least = lhs.getValue().compareTo(rhs.getValue()) <= 0 ? lhs.getValue() : rhs.getValue(); + return new LocalDateSegment<>(dateInterval, least); + } + return lhs == null ? new LocalDateSegment<>(dateInterval, rhs.getValue()) : new LocalDateSegment<>(dateInterval, lhs.getValue()); + } + + /** + * Basic combinator som tar største verdi, evt lhs dersom like + */ + public static > LocalDateSegment max(LocalDateInterval dateInterval, + LocalDateSegment lhs, + LocalDateSegment rhs) { + if (lhs != null && rhs != null) { + var greatest = lhs.getValue().compareTo(rhs.getValue()) >= 0 ? lhs.getValue() : rhs.getValue(); + return new LocalDateSegment<>(dateInterval, greatest); + } + return lhs == null ? new LocalDateSegment<>(dateInterval, rhs.getValue()) : new LocalDateSegment<>(dateInterval, lhs.getValue()); + } + + /** + * Basic combinator som slår sammen Liste-verdier til en liste + */ + public static LocalDateSegment> concatLists(LocalDateInterval dateInterval, + LocalDateSegment> lhs, + LocalDateSegment> rhs) { + if (lhs != null && rhs != null) { + return new LocalDateSegment<>(dateInterval, Stream.concat(lhs.getValue().stream(), rhs.getValue().stream()).toList()); + } else if (lhs == null && rhs == null) { + return null; + } + return lhs == null ? new LocalDateSegment<>(dateInterval, rhs.getValue()) : new LocalDateSegment<>(dateInterval, lhs.getValue()); + } + + /** + * Basic combinator som slår sammen to Sets vha Union + */ + public static LocalDateSegment> union(LocalDateInterval dateInterval, + LocalDateSegment> lhs, + LocalDateSegment> rhs) { + if (lhs != null && rhs != null) { + var union = new HashSet<>(lhs.getValue()); + union.addAll(rhs.getValue()); + return new LocalDateSegment<>(dateInterval, union); + } else if (lhs == null && rhs == null) { + return null; + } + return lhs == null ? new LocalDateSegment<>(dateInterval, rhs.getValue()) : new LocalDateSegment<>(dateInterval, lhs.getValue()); + } + + /** + * Basic combinator som slår sammen to Sets vha Intersection + */ + public static LocalDateSegment> intersection(LocalDateInterval dateInterval, + LocalDateSegment> lhs, + LocalDateSegment> rhs) { + if (lhs != null && rhs != null) { + var intersection = lhs.getValue().stream().filter(v -> rhs.getValue().contains(v)).collect(Collectors.toCollection(HashSet::new)); + return new LocalDateSegment<>(dateInterval, intersection); + } else if (lhs == null && rhs == null) { + return null; + } + return lhs == null ? new LocalDateSegment<>(dateInterval, rhs.getValue()) : new LocalDateSegment<>(dateInterval, lhs.getValue()); + } + + /** + * Basic combinator som slår sammen to Sets vha Difference (LHS-RHS) + */ + public static LocalDateSegment> difference(LocalDateInterval dateInterval, + LocalDateSegment> lhs, + LocalDateSegment> rhs) { + if (lhs != null && rhs != null) { + var difference = lhs.getValue().stream().filter(v -> !rhs.getValue().contains(v)).collect(Collectors.toCollection(HashSet::new)); + return new LocalDateSegment<>(dateInterval, difference); + } else if (lhs == null && rhs == null) { + return null; + } + return lhs == null ? new LocalDateSegment<>(dateInterval, rhs.getValue()) : new LocalDateSegment<>(dateInterval, lhs.getValue()); + } + + + @SuppressWarnings("unchecked") private static L sum(L lhs, R rhs) { if (lhs == null && rhs == null) { diff --git a/src/test/java/no/nav/fpsak/tidsserie/LocalDateTimelineExamplesTest.java b/src/test/java/no/nav/fpsak/tidsserie/LocalDateTimelineExamplesTest.java index c963b19..dd67d51 100644 --- a/src/test/java/no/nav/fpsak/tidsserie/LocalDateTimelineExamplesTest.java +++ b/src/test/java/no/nav/fpsak/tidsserie/LocalDateTimelineExamplesTest.java @@ -10,6 +10,7 @@ import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Set; import java.util.function.Function; import java.util.stream.Collectors; @@ -476,6 +477,64 @@ public void eksempel_splitt_av_tidsserie_ved_period_week_1() throws Exception { assertThat(mappedTimeline).isEqualTo(expectedTimeline); } + @Test + public void min_comparable_test() { + + var tidligDato = LocalDate.of(2019,11,15); + var senereDato = LocalDate.of(2020,6,1); + + var timelineA = new LocalDateTimeline<>( + List.of( + toSegment("2019-12-01", "2020-01-02", tidligDato), + toSegment("2020-02-03", "2020-02-16", tidligDato), + toSegment("2020-03-01", "2020-04-30", tidligDato))); + + var timelineB = new LocalDateTimeline<>( + List.of( + toSegment("2019-12-01", "2020-01-02", senereDato), + toSegment("2020-02-03", "2020-02-29", senereDato), + toSegment("2020-04-01", "2020-06-01", senereDato))); + + var timelineBMin = timelineB.combine(timelineA, StandardCombinators::min, JoinStyle.LEFT_JOIN); + + var expectedTimeline = new LocalDateTimeline<>( + List.of( + toSegment("2019-12-01", "2020-01-02", tidligDato), + toSegment("2020-02-03", "2020-02-16", tidligDato), + toSegment("2020-02-17", "2020-02-29", senereDato), + toSegment("2020-04-01", "2020-04-30", tidligDato), + toSegment("2020-05-01", "2020-06-01", senereDato))); + + assertThat(timelineBMin).isEqualTo(expectedTimeline); + } + + @Test + public void set_difference() { + + var timelineA = new LocalDateTimeline>( + List.of( + toSegment("2019-12-01", "2020-01-02", Set.of(1,2,3)), + toSegment("2020-02-03", "2020-02-16", Set.of(1,2,3,4,5,6)), + toSegment("2020-03-01", "2020-04-30", Set.of(1,2,3,4,5,6,7,8,9)))); + + var timelineB = new LocalDateTimeline>( + List.of( + toSegment("2019-12-01", "2020-01-02", Set.of()), + toSegment("2020-02-03", "2020-02-29", Set.of(1,2,3)), + toSegment("2020-04-01", "2020-06-01", Set.of(1,2,3,4,5,6)))); + + var timelineBMin = timelineA.combine(timelineB, StandardCombinators::difference, JoinStyle.LEFT_JOIN); + + var expectedTimeline = new LocalDateTimeline>( + List.of( + toSegment("2019-12-01", "2020-01-02", Set.of(1,2,3)), + toSegment("2020-02-03", "2020-02-16", Set.of(4,5,6)), + toSegment("2020-03-01", "2020-03-31", Set.of(1,2,3,4,5,6,7,8,9)), + toSegment("2020-04-01", "2020-04-30", Set.of(7,8,9)))); + + assertThat(timelineBMin).isEqualTo(expectedTimeline); + } + private static LocalDateSegment toSegment(String dt1, String dt2, V val) { return new LocalDateSegment<>(LocalDate.parse(dt1), LocalDate.parse(dt2), val); }