diff --git a/src/main/java/net/kemitix/text/fit/StreamZipper.java b/src/main/java/net/kemitix/text/fit/StreamZipper.java new file mode 100644 index 0000000..4fe001a --- /dev/null +++ b/src/main/java/net/kemitix/text/fit/StreamZipper.java @@ -0,0 +1,60 @@ +package net.kemitix.text.fit; + + +import java.util.List; +import java.util.function.BiFunction; +import java.util.function.IntFunction; +import java.util.stream.Stream; + +import static java.lang.Math.min; +import static java.util.stream.IntStream.range; + +/** + * Utility to zip two {@link Stream}s together. + * + * @author Paul Campbell (pcampbell@kemitix.net) + */ +public final class StreamZipper { + + private StreamZipper() { + throw new UnsupportedOperationException(); + } + + /** + * Zip two {@link Stream}s together. + * + *

The resulting stream will contain only as many items as the shortest of the two lists.

+ * + * @param a the first List + * @param b the second List + * @param zipper the function to zip an item from each list + * @param the type of the first list + * @param the type of the second list + * @param the type of the joined items + * @return a Stream of the joined items + */ + public static Stream zip( + final List a, + final List b, + final BiFunction zipper + ) { + return range(0, limit(a, b)) + .mapToObj(tuple(a, b, zipper)); + } + + private static int limit( + final List a, + final List b + ) { + return min(a.size(), b.size()); + } + + private static IntFunction tuple( + final List a, + final List b, + final BiFunction zipper + ) { + return i -> zipper.apply(a.get(i), b.get(i)); + } + +} diff --git a/src/main/java/net/kemitix/text/fit/Tuple.java b/src/main/java/net/kemitix/text/fit/Tuple.java new file mode 100644 index 0000000..086b690 --- /dev/null +++ b/src/main/java/net/kemitix/text/fit/Tuple.java @@ -0,0 +1,24 @@ +package net.kemitix.text.fit; + +public class Tuple { + + private final A partA; + private final B partB; + + private Tuple(final A partA, final B partB) { + this.partA = partA; + this.partB = partB; + } + + public static Tuple of(final A a, final B b) { + return new Tuple<>(a, b); + } + + public A get1() { + return partA; + } + + public B get2() { + return partB; + } +} diff --git a/src/test/java/net/kemitix/text/fit/StreamZipperTest.java b/src/test/java/net/kemitix/text/fit/StreamZipperTest.java new file mode 100644 index 0000000..5ed3015 --- /dev/null +++ b/src/test/java/net/kemitix/text/fit/StreamZipperTest.java @@ -0,0 +1,40 @@ +package net.kemitix.text.fit; + +import org.assertj.core.api.WithAssertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.lang.reflect.Constructor; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +class StreamZipperTest implements WithAssertions { + + @Test + void privateUtilityConstructor() throws NoSuchMethodException { + //given + final Constructor constructor = StreamZipper.class.getDeclaredConstructor(); + constructor.setAccessible(true); + //then + assertThatCode(constructor::newInstance) + .hasCauseInstanceOf(UnsupportedOperationException.class); + } + + @Test + @DisplayName("Pair two lists together") + void pairItems() { + //when + final List strings = Arrays.asList("One", "Two", "Three"); + final List integers = Arrays.asList(3, 2, 1); + final List> zipped = + StreamZipper.zip(strings, integers, Tuple::of).collect(Collectors.toList()); + //then + assertThat(zipped) + .extracting(Tuple::get1) + .containsExactlyElementsOf(strings.subList(0, zipped.size())); + assertThat(zipped) + .extracting(Tuple::get2) + .containsExactlyElementsOf(integers.subList(0, zipped.size())); + } +}