diff --git a/src/test/java/lambda/part3/exercise/Mapping.java b/src/test/java/lambda/part3/exercise/Mapping.java index c0a814a..3ed1e40 100644 --- a/src/test/java/lambda/part3/exercise/Mapping.java +++ b/src/test/java/lambda/part3/exercise/Mapping.java @@ -5,16 +5,14 @@ import data.Person; import org.junit.Test; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.function.BiConsumer; +import java.util.*; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Predicate; +import java.util.stream.Collectors; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; public class Mapping { @@ -81,7 +79,7 @@ public void mapping() { .map(TODO) // add 1 year to experience duration .map(e -> e.withJobHistory(addOneYear(e.getJobHistory()))) .map(TODO) // replace qa with QA * */ - .getList(); + .getList(); final List expectedResult = Arrays.asList( @@ -165,7 +163,193 @@ public LazyFlatMapHelper flatMap(Function> f) { } } + interface ReachIterable { + boolean tryGet(Consumer consumer); + + static ReachIterable from(List list) { + final Iterator iterator = list.iterator(); + return new ReachIterable() { + @Override + public boolean tryGet(Consumer consumer) { + if (iterator.hasNext()) { + consumer.accept(iterator.next()); + return true; + } else { + return false; + } + } + }; + } + + default List force() { + final List result = new ArrayList<>(); + while (tryGet(result::add)); + return result; + } + + default ReachIterable filter(Predicate predicate) { + final ReachIterable self = this; + return new ReachIterable() { + @Override + public boolean tryGet(Consumer consumer) { + final Boolean[] booleans = {false}; + self.tryGet(t -> { + if (predicate.test(t)) { + booleans[0] = true; + consumer.accept(t); + } + }); + + return booleans[0]; + } + }; + } + + default ReachIterable map(final Function function) { + final ReachIterable self = this; + return new ReachIterable() { + @Override + public boolean tryGet(final Consumer consumer) { + return self.tryGet(t -> consumer.accept(function.apply(t))); + } + }; + } + + default ReachIterable flatMap(Function> function) { + final ReachIterable self = this; + return new ReachIterable() { + private ReachIterable reachIterable; + @Override + public boolean tryGet(final Consumer consumer) { + boolean res = self.tryGet(t -> reachIterable = function.apply(t)); + while (reachIterable.tryGet(consumer)); + return res; + } + }; + } + + default boolean anyMatch(Predicate p) { + return firstMatch(p).isPresent(); + } + + default boolean allMatch(Predicate predicate) { + return !anyMatch(predicate.negate()); + } + + default boolean noneMatch(Predicate predicate) { + return !anyMatch(predicate); + } + + default Optional firstMatch(Predicate predicate) { + final Object[] objects = new Object[1]; + while (tryGet(t -> { + if (predicate.test(t)) { + objects[0] = t; + } + })) { + if (objects[0] != null) { + return Optional.of((T)objects[0]); + } + } + return Optional.empty(); + } + } + + @Test + public void reachIterable() { + final List integers = ReachIterable.from(Arrays.asList("1", "2", "3", "4", "5")) + .map(Integer::valueOf) + .filter(i -> i % 2 == 0) + .force(); + + assertEquals(integers, Arrays.asList(2,4)); + + final Optional stringOptional = ReachIterable.from(Arrays.asList("1", "2", "3", "4", "5")) + .firstMatch(s -> s.equals("3")); + assertEquals(stringOptional, Optional.of("3")); + + assertEquals(ReachIterable.from(Collections.singletonList("1")).firstMatch(s -> s.length() == 5), Optional.empty()); + + assertTrue(ReachIterable.from(Arrays.asList("1", "2", "3")) + .noneMatch(s -> s.length() == 3)); + + assertTrue(ReachIterable.from(Arrays.asList("1", "2", "3")) + .allMatch(s -> s.length() == 1)); + + + final List integers2 = ReachIterable.from(Arrays.asList(new String[][]{{"1", "2"}, {"3", "4"}, {"5", "6"}, {}})) + .flatMap(strings -> ReachIterable.from(Arrays.stream(strings).collect(Collectors.toList()))) + .map(Integer::valueOf) + .filter(i -> i % 2 == 0) + .force(); + + assertEquals(integers2, Arrays.asList(2,4,6)); + } + + interface Traversable { + void forEach(Consumer consumer); + + default Traversable flatMap(Function> function) { + final Traversable self = this; + return new Traversable() { + @Override + public void forEach(Consumer consumer) { + self.forEach(t -> function.apply(t).forEach(consumer)); + } + }; + } + + default Traversable filter(Predicate predicate) { + final Traversable self = this; + return new Traversable() { + @Override + public void forEach(Consumer consumer) { + self.forEach(t -> { + if (predicate.test(t)) { + consumer.accept(t); + } + }); + } + }; + } + + default Traversable map(Function function) { + final Traversable self = this; + return new Traversable() { + @Override + public void forEach(Consumer consumer) { + self.forEach(t -> consumer.accept(function.apply(t))); + } + }; + } + + default List toList() { + final List result = new ArrayList<>(); + forEach(result::add); + return result; + } + + static Traversable from(List list) { + return new Traversable() { + @Override + public void forEach(Consumer consumer) { + list.forEach(consumer); + } + }; + } + } + + @Test + public void traversable() { + final List integers = Traversable.from(Arrays.asList(new String[][]{{"1", "2"}, {"3", "4"}, {"5", "6"}})) + .flatMap(strings -> Traversable.from(Arrays.stream(strings).collect(Collectors.toList()))) + .map(Integer::valueOf) + .filter(i -> i % 2 == 0) + .toList(); + + assertEquals(integers, Arrays.asList(2,4,6)); + } @Test public void lazy_mapping() { @@ -198,7 +382,7 @@ public void lazy_mapping() { .map(TODO) // add 1 year to experience duration .map(TODO) // replace qa with QA * */ - .force(); + .force(); final List expectedResult = Arrays.asList(