From f4d6256f5a42a554fe7ad539d96c05c3098fab56 Mon Sep 17 00:00:00 2001
From: Stuart Marks <stuart.marks@oracle.com>
Date: Tue, 30 Aug 2016 11:18:14 -0700
Subject: [PATCH] Editing on hints. Rewrite g4_splitCharacterRuns().

---
 LambdaLab/test/exercises/D_SimpleStreams.java | 10 +++-
 .../test/exercises/E_IntermediateStreams.java | 16 ++++--
 .../test/exercises/F_AdvancedStreams.java     | 12 +++-
 LambdaLab/test/exercises/G_Challenges.java    | 41 +++++++++++++-
 LambdaLab/test/solutions/D_SimpleStreams.java | 10 +++-
 .../test/solutions/E_IntermediateStreams.java | 22 +++++---
 .../test/solutions/F_AdvancedStreams.java     | 12 +++-
 LambdaLab/test/solutions/G_Challenges.java    | 56 +++++++++++++++----
 8 files changed, 142 insertions(+), 37 deletions(-)

diff --git a/LambdaLab/test/exercises/D_SimpleStreams.java b/LambdaLab/test/exercises/D_SimpleStreams.java
index 6e2e31b..23e27ea 100644
--- a/LambdaLab/test/exercises/D_SimpleStreams.java
+++ b/LambdaLab/test/exercises/D_SimpleStreams.java
@@ -47,7 +47,8 @@ public class D_SimpleStreams {
     // </editor-fold>
     // Hint 2:
     // <editor-fold defaultstate="collapsed">
-    // Use collect() to create the result list.
+    // To create the result list, use collect() with one of the
+    // predefined collectors on the Collectors class.
     // </editor-fold>
 
 
@@ -111,12 +112,17 @@ public class D_SimpleStreams {
     }
     // Hint 1:
     // <editor-fold defaultstate="collapsed">
-    // Use Stream.mapToInt() to convert to IntStream.
+    // Use Stream.mapToInt() to convert a stream of objects to an IntStream.
     // </editor-fold>
     // Hint 2:
     // <editor-fold defaultstate="collapsed">
     // Look at java.util.OptionalInt to get the result.
     // </editor-fold>
+    // Hint 3:
+    // <editor-fold defaultstate="collapsed">
+    // Think about the case where the OptionalInt might be empty
+    // (that is, where it has no value).
+    // </editor-fold>
 
 
     /**
diff --git a/LambdaLab/test/exercises/E_IntermediateStreams.java b/LambdaLab/test/exercises/E_IntermediateStreams.java
index 80062e7..55bff1f 100644
--- a/LambdaLab/test/exercises/E_IntermediateStreams.java
+++ b/LambdaLab/test/exercises/E_IntermediateStreams.java
@@ -11,7 +11,7 @@ import java.util.Comparator;
 import java.util.List;
 import java.util.regex.Pattern;
 import java.util.stream.Collectors;
-import java.util.stream.IntStream;
+import java.util.stream.LongStream;
 
 import org.junit.After;
 import org.junit.Before;
@@ -52,7 +52,7 @@ public class E_IntermediateStreams {
 
     /**
      * Collect all the words from the text file into a list.
-     * Use the regular expression pattern WORD_PATTERN to split
+     * Use the regular expression Pattern WORD_PATTERN to split
      * a string into words, and use Pattern.splitAsStream(String)
      * to do the splitting. WORD_PATTERN is defined at the bottom
      * of this file.
@@ -178,9 +178,15 @@ public class E_IntermediateStreams {
 
         assertEquals(new BigInteger("51090942171709440000"), result);
     }
-    // Hint:
+    // Hint 1:
+    // <editor-fold defaultstate="collapsed">
+    // Use one of the range methods of LongStream to help create
+    // the BigInteger instances.
+    // </editor-fold>
+    // Hint 2:
     // <editor-fold defaultstate="collapsed">
-    // Use LongStream and reduction.
+    // Use Stream.reduce() to "collapse" all elements of a stream into
+    // a single value.
     // </editor-fold>
 
 
@@ -197,7 +203,7 @@ public class E_IntermediateStreams {
     }
     // Hint:
     // <editor-fold defaultstate="collapsed">
-    // Use Stream.reduce().
+    // Use Stream.reduce() and think about the order of the arguments.
     // </editor-fold>
 
 
diff --git a/LambdaLab/test/exercises/F_AdvancedStreams.java b/LambdaLab/test/exercises/F_AdvancedStreams.java
index 6327f29..b359a2a 100644
--- a/LambdaLab/test/exercises/F_AdvancedStreams.java
+++ b/LambdaLab/test/exercises/F_AdvancedStreams.java
@@ -91,7 +91,8 @@ public class F_AdvancedStreams {
     }
     // Hint 1:
     // <editor-fold defaultstate="collapsed">
-    // Use the "downstream" overload of Collectors.groupingBy().
+    // Use the overload of Collectors.groupingBy() that has
+    // a "downstream" parameter.
     // </editor-fold>
     // Hint 2:
     // <editor-fold defaultstate="collapsed">
@@ -162,8 +163,8 @@ public class F_AdvancedStreams {
     }
     // Hint 1:
     // <editor-fold defaultstate="collapsed">
-    // The nested map structure that's the desired is the result of applying
-    // a "downstream" collector that's the same operation as the first-level collector.
+    // The nested map structure that's desired is the result of applying a
+    // "downstream" collector that's the same operation as the first-level collector.
     // </editor-fold>
     // Hint 2:
     // <editor-fold defaultstate="collapsed">
@@ -263,6 +264,11 @@ public class F_AdvancedStreams {
         int getTotalCount() { return count; }
         int getDistinctCount() { return set.size(); }
     }
+    // Hint:
+    // <editor-fold defaultstate="collapsed">
+    // The operations you need to write are actually quite simple.
+    // Don't overthink it.
+    // </editor-fold>
 
     @Test @Ignore
     public void f7_countTotalAndDistinctWords() {
diff --git a/LambdaLab/test/exercises/G_Challenges.java b/LambdaLab/test/exercises/G_Challenges.java
index 58d8d9f..e1738ba 100644
--- a/LambdaLab/test/exercises/G_Challenges.java
+++ b/LambdaLab/test/exercises/G_Challenges.java
@@ -109,6 +109,10 @@ public class G_Challenges {
      * in the value sets for both p and q in the input map. Therefore,
      * in the result map, there should be a mapping with 20 as the key
      * and p and q as its value set.
+     *
+     * It is possible to accomplish this task using a single stream
+     * pipeline (not counting nested streams), that is, in a single pass
+     * over the input, without storing anything in a temporary collection.
      */
     @Test @Ignore
     public void g2_invertMultiMap() {
@@ -128,6 +132,17 @@ public class G_Challenges {
         assertEquals(new HashSet<>(Arrays.asList("d", "e", "f")), result.get(4));
         assertEquals(4, result.size());
     }
+    // Hint 1:
+    // <editor-fold defaultstate="collapsed">
+    // A general approach is to flatten the input structure in one stage
+    // of the pipeline and then to create the result structure using a collector.
+    // </editor-fold>
+    // Hint 2:
+    // <editor-fold defaultstate="collapsed">
+    // A useful intermediate data structure after the flattening step
+    // is a pair of items. You can write your own pair class, or you can
+    // use a pre-existing class like AbstractMap.SimpleEntry.
+    // </editor-fold>
 
 
     /**
@@ -158,7 +173,6 @@ public class G_Challenges {
 
 
 
-
     /**
      * Given a string, split it into a list of strings consisting of
      * consecutive characters from the original string. Note: this is
@@ -173,6 +187,12 @@ public class G_Challenges {
 
         assertEquals("[aaaaa, bb, cccc, d, eeeeee, aaa, fff]", result.toString());
     }
+    // Hint:
+    // <editor-fold defaultstate="collapsed">
+    // One possibility is a two-pass approach: one pass to gather data about
+    // the boundaries between the runs, and the second to create the substrings
+    // based on output from the first.
+    // </editor-fold>
 
     /**
      * Given a parallel stream of strings, collect them into a collection in reverse order.
@@ -188,7 +208,6 @@ public class G_Challenges {
             input.collect(Collector.of(null, null, null));
             // TODO fill in collector functions above
 
-
         assertEquals(
             IntStream.range(0, 100)
                      .map(i -> 99 - i)
@@ -196,6 +215,14 @@ public class G_Challenges {
                      .collect(Collectors.toList()),
             new ArrayList<>(result));
     }
+    // Hint 1:
+    // <editor-fold defaultstate="collapsed">
+    // ArrayDeque supports fast insertion at the front.
+    // </editor-fold>
+    // Hint 2:
+    // <editor-fold defaultstate="collapsed">
+    // Be careful with ordering of the arguments and results in the combiner.
+    // </editor-fold>
 
     /**
      * Given an array of int, find the int value that occurs a majority
@@ -212,6 +239,11 @@ public class G_Challenges {
     OptionalInt majority(int[] array) {
         return null; // TODO
     }
+    // Hint:
+    // <editor-fold defaultstate="collapsed">
+    // A two-pass approach may be called for here: a counting pass
+    // and a majority-finding pass.
+    // </editor-fold>
 
     @Test @Ignore
     public void g6_majority() {
@@ -238,6 +270,11 @@ public class G_Challenges {
     Supplier<Shoe> makeShoeSupplier(IntFunction<Shoe> ifunc, int size) {
         return null; // TODO
     }
+    // Hint:
+    // <editor-fold defaultstate="collapsed">
+    // You don't want to return the result of calling the IntFunction.
+    // Instead, you want to return a lambda that calls the IntFunction.
+    // </editor-fold>
 
     static class Shoe {
         final int size;
diff --git a/LambdaLab/test/solutions/D_SimpleStreams.java b/LambdaLab/test/solutions/D_SimpleStreams.java
index bdd2198..f31048d 100644
--- a/LambdaLab/test/solutions/D_SimpleStreams.java
+++ b/LambdaLab/test/solutions/D_SimpleStreams.java
@@ -57,7 +57,8 @@ public class D_SimpleStreams {
     // </editor-fold>
     // Hint 2:
     // <editor-fold defaultstate="collapsed">
-    // Use collect() to create the result list.
+    // To create the result list, use collect() with one of the
+    // predefined collectors on the Collectors class.
     // </editor-fold>
 
 
@@ -140,12 +141,17 @@ public class D_SimpleStreams {
     }
     // Hint 1:
     // <editor-fold defaultstate="collapsed">
-    // Use Stream.mapToInt() to convert to IntStream.
+    // Use Stream.mapToInt() to convert a stream of objects to an IntStream.
     // </editor-fold>
     // Hint 2:
     // <editor-fold defaultstate="collapsed">
     // Look at java.util.OptionalInt to get the result.
     // </editor-fold>
+    // Hint 3:
+    // <editor-fold defaultstate="collapsed">
+    // Think about the case where the OptionalInt might be empty
+    // (that is, where it has no value).
+    // </editor-fold>
 
 
     /**
diff --git a/LambdaLab/test/solutions/E_IntermediateStreams.java b/LambdaLab/test/solutions/E_IntermediateStreams.java
index 1f2717a..3f478d5 100644
--- a/LambdaLab/test/solutions/E_IntermediateStreams.java
+++ b/LambdaLab/test/solutions/E_IntermediateStreams.java
@@ -11,7 +11,7 @@ import java.util.Comparator;
 import java.util.List;
 import java.util.regex.Pattern;
 import java.util.stream.Collectors;
-import java.util.stream.IntStream;
+import java.util.stream.LongStream;
 
 import org.junit.After;
 import org.junit.Before;
@@ -58,7 +58,7 @@ public class E_IntermediateStreams {
 
     /**
      * Collect all the words from the text file into a list.
-     * Use the regular expression pattern WORD_PATTERN to split
+     * Use the regular expression Pattern WORD_PATTERN to split
      * a string into words, and use Pattern.splitAsStream(String)
      * to do the splitting. WORD_PATTERN is defined at the bottom
      * of this file.
@@ -218,16 +218,22 @@ public class E_IntermediateStreams {
         //TODO//BigInteger result = BigInteger.ONE;
         //BEGINREMOVE
         BigInteger result =
-            IntStream.rangeClosed(1, 21)
-                     .mapToObj(n -> BigInteger.valueOf(n))
-                     .reduce(BigInteger.ONE, (m, n) -> m.multiply(n));
+            LongStream.rangeClosed(1, 21)
+                      .mapToObj(BigInteger::valueOf)
+                      .reduce(BigInteger.ONE, BigInteger::multiply);
         //ENDREMOVE
 
         assertEquals(new BigInteger("51090942171709440000"), result);
     }
-    // Hint:
+    // Hint 1:
+    // <editor-fold defaultstate="collapsed">
+    // Use one of the range methods of LongStream to help create
+    // the BigInteger instances.
+    // </editor-fold>
+    // Hint 2:
     // <editor-fold defaultstate="collapsed">
-    // Use LongStream and reduction.
+    // Use Stream.reduce() to "collapse" all elements of a stream into
+    // a single value.
     // </editor-fold>
 
 
@@ -251,7 +257,7 @@ public class E_IntermediateStreams {
     }
     // Hint:
     // <editor-fold defaultstate="collapsed">
-    // Use Stream.reduce().
+    // Use Stream.reduce() and think about the order of the arguments.
     // </editor-fold>
 
 
diff --git a/LambdaLab/test/solutions/F_AdvancedStreams.java b/LambdaLab/test/solutions/F_AdvancedStreams.java
index f5c15e2..2fcb974 100644
--- a/LambdaLab/test/solutions/F_AdvancedStreams.java
+++ b/LambdaLab/test/solutions/F_AdvancedStreams.java
@@ -103,7 +103,8 @@ public class F_AdvancedStreams {
     }
     // Hint 1:
     // <editor-fold defaultstate="collapsed">
-    // Use the "downstream" overload of Collectors.groupingBy().
+    // Use the overload of Collectors.groupingBy() that has
+    // a "downstream" parameter.
     // </editor-fold>
     // Hint 2:
     // <editor-fold defaultstate="collapsed">
@@ -197,8 +198,8 @@ public class F_AdvancedStreams {
     }
     // Hint 1:
     // <editor-fold defaultstate="collapsed">
-    // The nested map structure that's the desired is the result of applying
-    // a "downstream" collector that's the same operation as the first-level collector.
+    // The nested map structure that's desired is the result of applying a
+    // "downstream" collector that's the same operation as the first-level collector.
     // </editor-fold>
     // Hint 2:
     // <editor-fold defaultstate="collapsed">
@@ -326,6 +327,11 @@ public class F_AdvancedStreams {
         int getTotalCount() { return count; }
         int getDistinctCount() { return set.size(); }
     }
+    // Hint:
+    // <editor-fold defaultstate="collapsed">
+    // The operations you need to write are actually quite simple.
+    // Don't overthink it.
+    // </editor-fold>
 
     @Test
     public void f7_countTotalAndDistinctWords() {
diff --git a/LambdaLab/test/solutions/G_Challenges.java b/LambdaLab/test/solutions/G_Challenges.java
index cd8ce39..ec6723f 100644
--- a/LambdaLab/test/solutions/G_Challenges.java
+++ b/LambdaLab/test/solutions/G_Challenges.java
@@ -131,6 +131,10 @@ public class G_Challenges {
      * in the value sets for both p and q in the input map. Therefore,
      * in the result map, there should be a mapping with 20 as the key
      * and p and q as its value set.
+     *
+     * It is possible to accomplish this task using a single stream
+     * pipeline (not counting nested streams), that is, in a single pass
+     * over the input, without storing anything in a temporary collection.
      */
     @Test
     public void g2_invertMultiMap() {
@@ -171,6 +175,17 @@ public class G_Challenges {
         assertEquals(new HashSet<>(Arrays.asList("d", "e", "f")), result.get(4));
         assertEquals(4, result.size());
     }
+    // Hint 1:
+    // <editor-fold defaultstate="collapsed">
+    // A general approach is to flatten the input structure in one stage
+    // of the pipeline and then to create the result structure using a collector.
+    // </editor-fold>
+    // Hint 2:
+    // <editor-fold defaultstate="collapsed">
+    // A useful intermediate data structure after the flattening step
+    // is a pair of items. You can write your own pair class, or you can
+    // use a pre-existing class like AbstractMap.SimpleEntry.
+    // </editor-fold>
 
 
     /**
@@ -216,7 +231,7 @@ public class G_Challenges {
                 len = slen;
                 list.clear();
                 list.add(s);
-            } // ignore if slen < len
+            } // ignore input string if slen < len
         }
 
         Longest comb(Longest other) {
@@ -237,7 +252,6 @@ public class G_Challenges {
     //ENDREMOVE
 
 
-
     /**
      * Given a string, split it into a list of strings consisting of
      * consecutive characters from the original string. Note: this is
@@ -250,23 +264,26 @@ public class G_Challenges {
 
         //TODO//List<String> result = null;
         //BEGINREMOVE
-
-        List<Integer> bounds =
+        int[] bounds =
             IntStream.rangeClosed(0, input.length())
                      .filter(i -> i == 0 || i == input.length() ||
                                   input.charAt(i-1) != input.charAt(i))
-                     .boxed()
-                     .collect(Collectors.toList());
+                     .toArray();
 
         List<String> result =
-            IntStream.range(1, bounds.size())
-                     .mapToObj(i -> input.substring(bounds.get(i-1), bounds.get(i)))
+            IntStream.range(1, bounds.length)
+                     .mapToObj(i -> input.substring(bounds[i-1], bounds[i]))
                      .collect(Collectors.toList());
-
         //ENDREMOVE
 
         assertEquals("[aaaaa, bb, cccc, d, eeeeee, aaa, fff]", result.toString());
     }
+    // Hint:
+    // <editor-fold defaultstate="collapsed">
+    // One possibility is a two-pass approach: one pass to gather data about
+    // the boundaries between the runs, and the second to create the substrings
+    // based on output from the first.
+    // </editor-fold>
 
     /**
      * Given a parallel stream of strings, collect them into a collection in reverse order.
@@ -281,14 +298,11 @@ public class G_Challenges {
         //UNCOMMENT//Collection<String> result =
         //UNCOMMENT//    input.collect(Collector.of(null, null, null));
         //UNCOMMENT//    // TODO fill in collector functions above
-
         //BEGINREMOVE
-
         Collection<String> result =
             input.collect(Collector.of(ArrayDeque::new,
                                        ArrayDeque::addFirst,
                                        (d1, d2) -> { d2.addAll(d1); return d2; }));
-
         //ENDREMOVE
 
         assertEquals(
@@ -298,6 +312,14 @@ public class G_Challenges {
                      .collect(Collectors.toList()),
             new ArrayList<>(result));
     }
+    // Hint 1:
+    // <editor-fold defaultstate="collapsed">
+    // ArrayDeque supports fast insertion at the front.
+    // </editor-fold>
+    // Hint 2:
+    // <editor-fold defaultstate="collapsed">
+    // Be careful with ordering of the arguments and results in the combiner.
+    // </editor-fold>
 
     /**
      * Given an array of int, find the int value that occurs a majority
@@ -326,6 +348,11 @@ public class G_Challenges {
                   .findAny();
         //ENDREMOVE
     }
+    // Hint:
+    // <editor-fold defaultstate="collapsed">
+    // A two-pass approach may be called for here: a counting pass
+    // and a majority-finding pass.
+    // </editor-fold>
 
     @Test
     public void g6_majority() {
@@ -355,6 +382,11 @@ public class G_Challenges {
         return () -> ifunc.apply(size);
         //ENDREMOVE
     }
+    // Hint:
+    // <editor-fold defaultstate="collapsed">
+    // You don't want to return the result of calling the IntFunction.
+    // Instead, you want to return a lambda that calls the IntFunction.
+    // </editor-fold>
 
     static class Shoe {
         final int size;
-- 
GitLab