From 348386cc53d0d0ed4386718d396e4ad9f3c0f662 Mon Sep 17 00:00:00 2001
From: MortyMerr <anton.nazarof@mail.ru>
Date: Thu, 6 Jul 2017 15:45:52 +0300
Subject: [PATCH 1/2] part-3

---
 .../java/lambda/part3/exercise/Mapping.java   | 82 ++++++++++++-------
 1 file changed, 52 insertions(+), 30 deletions(-)

diff --git a/src/test/java/lambda/part3/exercise/Mapping.java b/src/test/java/lambda/part3/exercise/Mapping.java
index c0a814a..af0a585 100644
--- a/src/test/java/lambda/part3/exercise/Mapping.java
+++ b/src/test/java/lambda/part3/exercise/Mapping.java
@@ -3,6 +3,7 @@
 import data.Employee;
 import data.JobHistoryEntry;
 import data.Person;
+import jdk.nashorn.internal.scripts.JO;
 import org.junit.Test;
 
 import java.util.ArrayList;
@@ -18,6 +19,15 @@
 
 public class Mapping {
 
+    private List<JobHistoryEntry> addOneYear(List<JobHistoryEntry> jobs) {
+        return new MapHelper<>(jobs).map(j -> j.withDuration(j.getDuration() + 1)).getList();
+    }
+
+    private List<JobHistoryEntry> upperCaseQa(List<JobHistoryEntry> jobs) {
+        return new MapHelper<>(jobs).map(j -> j.getPosition().equals("qa") ? j.withPosition(j.getPosition().toUpperCase()) : j).getList();
+        //TOASK how should i format this code? Should i use statement lambda for better reading?
+    }
+
     private static class MapHelper<T> {
         private final List<T> list;
 
@@ -32,8 +42,11 @@ public List<T> getList() {
         // [T] -> (T -> R) -> [R]
         // [T1, T2, T3] -> (T -> R) -> [R1, R2, R3]
         public <R> MapHelper<R> map(Function<T, R> f) {
-            // TODO
-            throw new UnsupportedOperationException();
+            final List<R> result = new ArrayList<>();
+            for (T t : list) {
+                result.add(f.apply(t));
+            }
+            return new MapHelper<>(result);
         }
 
         // [T] -> (T -> [R]) -> [R]
@@ -41,12 +54,12 @@ public <R> MapHelper<R> map(Function<T, R> f) {
         // map: [T, T, T], T -> [R] => [[], [R1, R2], [R3, R4, R5]]
         // flatMap: [T, T, T], T -> [R] => [R1, R2, R3, R4, R5]
         public <R> MapHelper<R> flatMap(Function<T, List<R>> f) {
-            final List<R> result = new ArrayList<R>();
+            final List<R> result = new ArrayList<>();
             list.forEach((T t) ->
-                    f.apply(t).forEach(result::add)
+                    result.addAll(f.apply(t))
             );
 
-            return new MapHelper<R>(result);
+            return new MapHelper<>(result);
         }
     }
 
@@ -76,12 +89,9 @@ public void mapping() {
 
         final List<Employee> mappedEmployees =
                 new MapHelper<>(employees)
-                /*
-                .map(TODO) // change name to John .map(e -> e.withPerson(e.getPerson().withFirstName("John")))
-                .map(TODO) // add 1 year to experience duration .map(e -> e.withJobHistory(addOneYear(e.getJobHistory())))
-                .map(TODO) // replace qa with QA
-                * */
-                .getList();
+                        .map(e -> e.withPerson(e.getPerson().withFirstName("John")))
+                        .map(e -> e.withJobHistory(addOneYear(e.getJobHistory())))
+                        .map(e -> e.withJobHistory(upperCaseQa(e.getJobHistory()))).getList();
 
         final List<Employee> expectedResult =
                 Arrays.asList(
@@ -110,8 +120,13 @@ public void mapping() {
 
 
     private static class LazyMapHelper<T, R> {
+        private final Function<T, R> function;
+        private final List<T> list;
+
 
         public LazyMapHelper(List<T> list, Function<T, R> function) {
+            this.list = list;
+            this.function = function;
         }
 
         public static <T> LazyMapHelper<T, T> from(List<T> list) {
@@ -119,35 +134,45 @@ public static <T> LazyMapHelper<T, T> from(List<T> list) {
         }
 
         public List<R> force() {
-            // TODO
-            throw new UnsupportedOperationException();
+            final List<R> result = new ArrayList<>();
+            for (T t : list) {
+                result.add(function.apply(t));
+            }
+            return result;
         }
 
         public <R2> LazyMapHelper<T, R2> map(Function<R, R2> f) {
-            // TODO
-            throw new UnsupportedOperationException();
+            return new LazyMapHelper<>(list, function.andThen(f));
         }
-
     }
 
     private static class LazyFlatMapHelper<T, R> {
+        private List<T> list;
+        private Function<T, List<R>> function;
 
         public LazyFlatMapHelper(List<T> list, Function<T, List<R>> function) {
+            this.list = list;
+            this.function = function;
         }
 
         public static <T> LazyFlatMapHelper<T, T> from(List<T> list) {
-            throw new UnsupportedOperationException();
+            return new LazyFlatMapHelper<>(list, Arrays::asList);
         }
 
         public List<R> force() {
-            // TODO
-            throw new UnsupportedOperationException();
+            final List<R> result = new ArrayList<>();
+            for (T t : list) {
+                result.addAll(function.apply(t));
+            }
+            return result;
         }
 
-        // TODO filter
         // (T -> boolean) -> (T -> [T])
         // filter: [T1, T2] -> (T -> boolean) -> [T2]
         // flatMap": [T1, T2] -> (T -> [T]) -> [T2]
+        public LazyFlatMapHelper<T, R> filter(final Predicate<T> predicate) {
+            return new LazyFlatMapHelper<>(list, e -> predicate.test(e) ? function.apply(e) : Collections.emptyList());
+        }
 
         public <R2> LazyFlatMapHelper<T, R2> map(Function<R, R2> f) {
             final Function<R, List<R2>> listFunction = rR2TorListR2(f);
@@ -156,17 +181,15 @@ public <R2> LazyFlatMapHelper<T, R2> map(Function<R, R2> f) {
 
         // (R -> R2) -> (R -> [R2])
         private <R2> Function<R, List<R2>> rR2TorListR2(Function<R, R2> f) {
-            throw new UnsupportedOperationException();
+            return e -> Collections.singletonList(f.apply(e));
         }
 
-        // TODO *
         public <R2> LazyFlatMapHelper<T, R2> flatMap(Function<R, List<R2>> f) {
-            throw new UnsupportedOperationException();
+            return new LazyFlatMapHelper<>(list, t -> new MapHelper<>(function.apply(t)).flatMap(f).getList());
         }
     }
 
 
-
     @Test
     public void lazy_mapping() {
         final List<Employee> employees =
@@ -193,12 +216,9 @@ public void lazy_mapping() {
 
         final List<Employee> mappedEmployees =
                 LazyMapHelper.from(employees)
-                /*
-                .map(TODO) // change name to John
-                .map(TODO) // add 1 year to experience duration
-                .map(TODO) // replace qa with QA
-                * */
-                .force();
+                        .map(e -> e.withPerson(e.getPerson().withFirstName("John")))
+                        .map(e -> e.withJobHistory(addOneYear(e.getJobHistory())))
+                        .map(e -> e.withJobHistory(upperCaseQa(e.getJobHistory()))).force();
 
         final List<Employee> expectedResult =
                 Arrays.asList(
@@ -225,3 +245,5 @@ public void lazy_mapping() {
         assertEquals(mappedEmployees, expectedResult);
     }
 }
+
+

From e04846adff35b8604faa301c79edf46225e72b1c Mon Sep 17 00:00:00 2001
From: MortyMerr <anton.nazarof@mail.ru>
Date: Thu, 6 Jul 2017 20:23:25 +0300
Subject: [PATCH 2/2] ReachIter, Traversable

---
 .../java/lambda/part3/exercise/FilterMap.java |   2 +
 .../java/lambda/part3/exercise/Mapping.java   |  98 ++++++++++-
 .../part3/exercise/ReachIterableTest.java     | 152 ++++++++++++++++++
 3 files changed, 246 insertions(+), 6 deletions(-)
 create mode 100644 src/test/java/lambda/part3/exercise/ReachIterableTest.java

diff --git a/src/test/java/lambda/part3/exercise/FilterMap.java b/src/test/java/lambda/part3/exercise/FilterMap.java
index 178190e..30db765 100644
--- a/src/test/java/lambda/part3/exercise/FilterMap.java
+++ b/src/test/java/lambda/part3/exercise/FilterMap.java
@@ -2,6 +2,7 @@
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.function.Consumer;
 import java.util.function.Function;
 import java.util.function.Predicate;
 
@@ -30,6 +31,7 @@ public Function<T, R> getFunction() {
         }
     }
 
+
     public static class LazyCollectionHelper<T> {
         private final List<Container<Object, Object>> actions;
         private final List<T> list;
diff --git a/src/test/java/lambda/part3/exercise/Mapping.java b/src/test/java/lambda/part3/exercise/Mapping.java
index af0a585..ec8b81f 100644
--- a/src/test/java/lambda/part3/exercise/Mapping.java
+++ b/src/test/java/lambda/part3/exercise/Mapping.java
@@ -1,16 +1,12 @@
 package lambda.part3.exercise;
 
+import com.sun.org.apache.regexp.internal.RE;
 import data.Employee;
 import data.JobHistoryEntry;
 import data.Person;
-import jdk.nashorn.internal.scripts.JO;
 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;
@@ -189,6 +185,96 @@ public <R2> LazyFlatMapHelper<T, R2> flatMap(Function<R, List<R2>> f) {
         }
     }
 
+    interface Traversable<T> {
+        void forEach(Consumer<T> c);
+
+        default List<T> toList() {
+            final List<T> result = new ArrayList<>();
+            forEach(result::add);
+            return result;
+        }
+
+        static <T> Traversable<T> from(List<T> list) {
+            return new Traversable<T>() {
+                @Override
+                public void forEach(Consumer<T> c) {
+                    list.forEach(c);
+                }
+            };
+        }
+
+        default Traversable<T> filter(Predicate<T> p) {
+            return c -> this.forEach(v -> {
+                if (p.test(v)) {
+                    c.accept(v);
+                }
+            });
+        }
+
+        default <R> Traversable<R> flatMap(Function<T, Traversable<R>> f) {
+            return c -> this.forEach(t -> f.apply(t).forEach(c));
+        }
+
+        default <R> Traversable<R> map(Function<T, R> f) {
+            return c -> this.forEach(t -> c.accept(f.apply(t)));
+        }
+    }
+
+    @Test
+    public void traversable() {
+        final List<Employee> employees =
+                Arrays.asList(
+                        new Employee(
+                                new Person("a", "Galt", 30),
+                                Arrays.asList(
+                                        new JobHistoryEntry(2, "dev", "epam"),
+                                        new JobHistoryEntry(1, "dev", "google")
+                                )),
+                        new Employee(
+                                new Person("b", "Doe", 40),
+                                Arrays.asList(
+                                        new JobHistoryEntry(3, "qa", "yandex"),
+                                        new JobHistoryEntry(1, "qa", "epam"),
+                                        new JobHistoryEntry(1, "dev", "abc")
+                                )),
+                        new Employee(
+                                new Person("c", "White", 50),
+                                Collections.singletonList(
+                                        new JobHistoryEntry(5, "qa", "epam")
+                                ))
+                );
+
+        final List<Employee> filteredEmployees =
+                Traversable.from(employees)
+                        .filter(e -> e.getPerson().getFirstName().equals("a")).toList();
+
+        final List<Employee> expectedResult =
+                Arrays.asList(
+                        new Employee(
+                                new Person("a", "Galt", 30),
+                                Arrays.asList(
+                                        new JobHistoryEntry(2, "dev", "epam"),
+                                        new JobHistoryEntry(1, "dev", "google")
+                                ))
+                );
+
+        assertEquals(filteredEmployees, expectedResult);
+
+        final List<JobHistoryEntry> flattedJobs =
+                Traversable.from(employees)
+                        .filter(e -> e.getPerson().getLastName().length() < 5)
+                        .flatMap(e -> Traversable.from(e.getJobHistory())).toList();
+
+        final List<JobHistoryEntry> jobs = Arrays.asList(
+                new JobHistoryEntry(2, "dev", "epam"),
+                new JobHistoryEntry(1, "dev", "google"),
+                new JobHistoryEntry(3, "qa", "yandex"),
+                new JobHistoryEntry(1, "qa", "epam"),
+                new JobHistoryEntry(1, "dev", "abc")
+        );
+
+        assertEquals(flattedJobs, jobs);
+    }
 
     @Test
     public void lazy_mapping() {
diff --git a/src/test/java/lambda/part3/exercise/ReachIterableTest.java b/src/test/java/lambda/part3/exercise/ReachIterableTest.java
new file mode 100644
index 0000000..20ef599
--- /dev/null
+++ b/src/test/java/lambda/part3/exercise/ReachIterableTest.java
@@ -0,0 +1,152 @@
+package lambda.part3.exercise;
+
+import com.google.common.collect.FluentIterable;
+import com.sun.org.apache.xpath.internal.operations.Bool;
+import org.junit.Test;
+
+import javax.xml.ws.Holder;
+import java.util.*;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.Predicate;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+/**
+ * Created by antonnazarov on 06.07.17.
+ */
+public class ReachIterableTest {
+    interface ReachIterable<T> {
+        default boolean anyMatch(final Predicate<T> p) {
+            return firstMatch(p).isPresent();
+        }
+
+        default boolean allMatch(final Predicate<T> p) {
+            return !anyMatch(p.negate());
+        }
+
+        default boolean noneMatch(final Predicate<T> p) {
+            return !anyMatch(p);
+        }
+
+        default Optional<T> firstMatch(final Predicate<T> p) {
+            Holder<Optional<T>> founded = new Holder<>(Optional.empty());
+            while (tryGet(t -> {
+                if (p.test(t)) {
+                    founded.value = Optional.of(t);
+                }
+            }) && (!founded.value.isPresent())) {
+                ;
+            }
+
+            return founded.value;
+        }
+
+        default ReachIterable<T> filter(final Predicate<T> p) {
+            return c -> this.tryGet(v -> {
+                if (p.test(v)) {
+                    c.accept(v);
+                }
+            });
+        }
+
+
+        default <R> ReachIterable<R> map(final Function<T, R> f) {
+            return c -> this.tryGet(e -> c.accept(f.apply(e)));
+        }
+
+        default <R> ReachIterable<R> flatMap(final Function<T, ReachIterable<R>> f) {
+            //TOAPOLOGIZE Code in this method is not really my. I cheated to view the result.
+
+            ReachIterable<T> self = this;
+            return new ReachIterable<R>() {
+                ReachIterable<R> tmp;
+                boolean selfHasNext = false,
+                        tmpHasNext = false;
+
+                @Override
+                public boolean tryGet(final Consumer<R> c) {
+                    if (!tmpHasNext) {
+                        selfHasNext = self.tryGet(e -> tmp = f.apply(e));
+                    }
+
+                    tmpHasNext = tmp.tryGet(c);
+
+                    return tmpHasNext || selfHasNext;
+                }
+            };
+        }
+
+        static <T> ReachIterable<T> from(List<T> list) {
+            final Iterator<T> i = list.iterator();
+            return c -> {
+                if (i.hasNext()) {
+                    c.accept(i.next());
+                }
+                return i.hasNext();
+            };
+        }
+
+        default List<T> force() {
+            final List<T> result = new ArrayList<>();
+            while (tryGet(result::add)) {
+                ;
+            }
+            return result;
+        }
+
+        boolean tryGet(final Consumer<T> c);
+    }
+
+    private final List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
+
+    @Test
+    public void reachIterableFilter() {
+
+        List<Integer> filteredNumbers = ReachIterable.from(numbers).filter(t -> (t % 2) == 0).force();
+
+        assertEquals(filteredNumbers, Arrays.asList(2, 4));
+    }
+
+    @Test
+    public void reachIterableMap() {
+        final List<Integer> mappedNumbers = ReachIterable.from(numbers).map(t -> t * 2).force();
+        assertEquals(mappedNumbers, Arrays.asList(2, 4, 6, 8, 10));
+    }
+
+    @Test
+    public void reachIteableFlatted() {
+        final List<Integer> mappedNumbers = ReachIterable.from(numbers)
+                .filter(e -> e < 3)
+                .flatMap(e -> ReachIterable.from(Arrays.asList(e * 2, e * 3)))
+                .force();
+
+        assertEquals(mappedNumbers, Arrays.asList(2, 3, 4, 6));
+    }
+
+    @Test
+    public void anyMatches() {
+        assertEquals(ReachIterable.from(numbers).anyMatch(t -> t.equals(2)), true);
+        assertEquals(ReachIterable.from(numbers).anyMatch(t -> t.equals(7)), false);
+    }
+
+    @Test
+    public void firstMathces() {
+        assertEquals(ReachIterable.from(numbers).firstMatch(t -> t.equals(2)).get(), numbers.get(1));
+        assertEquals(ReachIterable.from(numbers).firstMatch(t -> t.equals(7)).isPresent(), false);
+    }
+
+    @Test
+    public void allMatches() {
+        assertEquals(ReachIterable.from(numbers).allMatch(e -> e < 10), true);
+        assertEquals(ReachIterable.from(numbers).allMatch(e -> e.equals(1)), false);
+    }
+
+    @Test
+    public void noneMatches() {
+        assertEquals(ReachIterable.from(numbers).noneMatch(e -> e.equals(1)), false);
+        assertEquals(ReachIterable.from(numbers).noneMatch(e -> e.equals(10)), true);
+    }
+}