{"id":"streams-reduce","slug":"reduce-and-intstream","title":"reduce, and IntStream","text":"Every terminal operation so far has had one fixed job: count, add up, collect. `reduce` is the general one: it combines all the values into one, in whatever way its lambda says.\n\n### reduce\n\n`reduce` takes two things: a starting value, and a lambda with two parameters, the result so far and the next value. The lambda returns the new result so far. The club's total distance, with `reduce`:\n\n```java\nArrayList<Integer> rides = new ArrayList<>();\nrides.add(42);\nrides.add(65);\nrides.add(18);\nint total = rides.stream()\n        .reduce(0, (sum, km) -> sum + km);\nSystem.out.println(total);\n```\n\n```output\n125\n```\n\nThe result starts as 0. Then the lambda runs once for each value: 0 + 42 is 42, 42 + 65 is 107, and 107 + 18 is 125. When the stream is empty, the lambda never runs, and `reduce` returns the starting value.\n\n`mapToInt(km -> km).sum()` gives the same total, and reads more easily. After `mapToInt`, an IntStream also has `max()`, but like `average()` it returns a box that may be empty (an OptionalInt, whose `getAsInt()` takes the `int` out). `reduce` can find the longest ride as a plain `int`, with 0 for an empty list: start at 0, and keep, at each value, the bigger of the result so far and the value:\n\n```java\nArrayList<Integer> rides = new ArrayList<>();\nrides.add(42);\nrides.add(65);\nrides.add(18);\nint longest = rides.stream()\n        .reduce(0, (longestSoFar, km) -> Math.max(longestSoFar, km));\nSystem.out.println(longest);\n```\n\n```output\n65\n```\n\nThe starting value takes part in the result, so pick one that changes nothing: 0 for a sum, `\"\"` for text. For the longest ride, 0 changes nothing only because no ride is shorter than 0 km. It's the same thought as the \"best so far\" of module 29: for the lowest temperature, 0 would be a bad start, since every reading could be above it.\n\n`reduce` works on Strings too. The first letters of the riders' names, joined together:\n\n```java\nArrayList<String> riders = new ArrayList<>();\nriders.add(\"Noor\");\nriders.add(\"Pablo\");\nriders.add(\"Kaia\");\nString initials = riders.stream()\n        .map(name -> name.substring(0, 1))\n        .reduce(\"\", (text, letter) -> text + letter);\nSystem.out.println(initials);\n```\n\n```output\nNPK\n```\n\n`map` first turns each name into its first letter, and `reduce` only joins the letters. That's how Java's documentation asks for `reduce` to be used: a start that changes nothing, and a lambda that only combines two values, so that the result doesn't depend on how the values are grouped. Any work on each value belongs in a `map` before `reduce`.\n\nIn this form, `reduce` gives a result of the same type as the stream's values: an Integer for a stream of Integers, a String for a stream of Strings. For a stream of objects, `map` them to the value you want to combine first.\n\n### IntStream: numbers without a list\n\nStep 3's IntStream, the stream of `int` values, can also be made from nothing but a range of numbers. `IntStream.range(1, 5)` is the numbers 1, 2, 3 and 4: the end isn't included, just like in `for (int i = 1; i < 5; i++)` and in `substring`'s end index. `IntStream.rangeClosed(1, 5)` includes the end, 5. IntStream lives in `java.util.stream`:\n\n```java\nimport java.util.stream.IntStream;\n\npublic class Main {\n    public static void main(String[] args) {\n        IntStream.range(1, 5)\n                .forEach(seat -> System.out.println(\"Seat \" + seat));\n        System.out.println(IntStream.rangeClosed(1, 10).sum());\n        long evenSeats = IntStream.rangeClosed(1, 20)\n                .filter(seat -> seat % 2 == 0)\n                .count();\n        System.out.println(evenSeats);\n    }\n}\n```\n\n```output\nSeat 1\nSeat 2\nSeat 3\nSeat 4\n55\n10\n```\n\n- A cinema's row with seats 1 to 4 comes from `range(1, 5)`.\n- `rangeClosed(1, 10).sum()` adds up 1 to 10.\n- An IntStream has the intermediate operations you know, such as `filter`, and its values are plain `int` values.\n\nA range can go through a list's indexes, `range(0, list.size())`, when the position matters, as in a numbered list:\n\n```java\nimport java.util.ArrayList;\nimport java.util.stream.IntStream;\n\npublic class Main {\n    public static void main(String[] args) {\n        ArrayList<String> films = new ArrayList<>();\n        films.add(\"Vertigo\");\n        films.add(\"Alien\");\n        films.add(\"Heat\");\n        IntStream.range(0, films.size())\n                .forEach(i -> System.out.println((i + 1) + \". \" + films.get(i)));\n    }\n}\n```\n\n```output\n1. Vertigo\n2. Alien\n3. Heat\n```\n\nThe lambda uses `films` from outside, which is fine: it only reads the list, and `films` is never given another list.\n","task":"What does this program print?\n","kind":"predict","seed":"import java.util.ArrayList;\nimport java.util.stream.IntStream;\n\npublic class Main {\n    public static void main(String[] args) {\n        ArrayList<Integer> rides = new ArrayList<>();\n        rides.add(30);\n        rides.add(45);\n        rides.add(40);\n\n        int longest = rides.stream()\n                .reduce(50, (longestSoFar, km) -> Math.max(longestSoFar, km));\n        int doubled = rides.stream()\n                .map(km -> 2 * km)\n                .reduce(0, (sum, km) -> sum + km);\n        System.out.println(longest + \" \" + doubled);\n        System.out.println(IntStream.range(2, 6).sum() + \" \" + IntStream.rangeClosed(2, 6).count());\n    }\n}\n","solution":"import java.util.ArrayList;\nimport java.util.stream.IntStream;\n\npublic class Main {\n    public static void main(String[] args) {\n        ArrayList<Integer> rides = new ArrayList<>();\n        rides.add(30);\n        rides.add(45);\n        rides.add(40);\n\n        int longest = rides.stream()\n                .reduce(50, (longestSoFar, km) -> Math.max(longestSoFar, km));\n        int doubled = rides.stream()\n                .map(km -> 2 * km)\n                .reduce(0, (sum, km) -> sum + km);\n        System.out.println(longest + \" \" + doubled);\n        System.out.println(IntStream.range(2, 6).sum() + \" \" + IntStream.rangeClosed(2, 6).count());\n    }\n}\n","hints":["The starting value of `reduce` takes part in the result, so with `Math.max` the result is never less than the start. The end of a `range` isn't included; the end of a `rangeClosed` is.","Every ride is shorter than 50, so the first `reduce` keeps 50. `map` doubles the rides to 60, 90 and 80, and the second `reduce` adds them up from 0. `range(2, 6)` is 2, 3, 4 and 5, and `rangeClosed(2, 6)` has five values, 2 to 6."],"tests":[{"name":"Output","stdin":"","expect":"50 230\n14 5","hidden":false}],"require":[],"forbid":[],"lines":["50 230","14 5"],"more":[{"task":"A cinema row has `seats` seats, numbered from 1. Fill in the blank so that the program prints a label for each seat of row C, the last one included. For the program as it is, it prints:\n\n```\nC1\nC2\nC3\nC4\nC5\nC6\n```\n","kind":"fill","seed":"import java.util.stream.IntStream;\n\npublic class Main {\n    public static void main(String[] args) {\n        int seats = 6;\n        IntStream.[[rangeClosed]](1, seats)\n                .forEach(seat -> System.out.println(\"C\" + seat));\n    }\n}\n","solution":"import java.util.stream.IntStream;\n\npublic class Main {\n    public static void main(String[] args) {\n        int seats = 6;\n        IntStream.rangeClosed(1, seats)\n                .forEach(seat -> System.out.println(\"C\" + seat));\n    }\n}\n","hints":["`range(1, seats)` would stop at 5: its end isn't included.","The kind of range that includes its end is `rangeClosed`."],"tests":[{"name":"Output","stdin":"","expect":"C1\nC2\nC3\nC4\nC5\nC6","hidden":false}],"require":[],"forbid":[]},{"task":"Write the method `longestRide(List<Integer> rides)`, which returns the longest ride, and 0 for an empty list. Use `reduce` with `Math.max`. The check runs:\n\n```java\nList<Integer> rides = new ArrayList<>();\nrides.add(42);\nrides.add(65);\nrides.add(18);\nSystem.out.println(longestRide(rides));\n```\n\nand expects:\n\n```\n65\n```\n","kind":"code","seed":"import java.util.ArrayList;\nimport java.util.List;\n\npublic class Main {\n    public static void main(String[] args) {\n        List<Integer> rides = new ArrayList<>();\n        rides.add(42);\n        rides.add(65);\n        rides.add(18);\n        System.out.println(longestRide(rides));\n    }\n\n    // Write the method longestRide here.\n}\n","solution":"import java.util.ArrayList;\nimport java.util.List;\n\npublic class Main {\n    public static void main(String[] args) {\n        List<Integer> rides = new ArrayList<>();\n        rides.add(42);\n        rides.add(65);\n        rides.add(18);\n        System.out.println(longestRide(rides));\n    }\n\n    public static int longestRide(List<Integer> rides) {\n        return rides.stream()\n                .reduce(0, (longest, km) -> Math.max(longest, km));\n    }\n}\n","hints":["The header is `public static int longestRide(List<Integer> rides)`. Start `reduce` at 0: that's also what it returns for an empty list.","`return rides.stream().reduce(0, (longest, km) -> Math.max(longest, km));`"],"tests":[{"name":"Test 1","stdin":"","call":"List<Integer> rides = new ArrayList<>();\nrides.add(42);\nrides.add(65);\nrides.add(18);\nSystem.out.println(longestRide(rides));\n","expect":"65","hidden":false},{"name":"Test 2","stdin":"","call":"List<Integer> rides = new ArrayList<>();\nSystem.out.println(longestRide(rides));\nrides.add(7);\nSystem.out.println(longestRide(rides));\nrides.add(120);\nrides.add(120);\nrides.add(3);\nSystem.out.println(longestRide(rides) + \" \" + rides.size());\n","expect":"0\n7\n120 4","hidden":true}],"require":[{"pattern":"\\bpublic\\s+static\\s+int\\s+longestRide\\s*\\(\\s*List<Integer>\\s+\\w+\\s*\\)","message":"Write the method as `public static int longestRide(List<Integer> rides)`."},{"pattern":"\\.reduce\\s*\\(","message":"Combine the rides with `reduce`."},{"pattern":"\\bMath\\s*\\.\\s*max\\b|\\bMath\\s*::\\s*max\\b","message":"Keep the bigger value with `Math.max`."}],"forbid":[{"pattern":"\\b(for|while)\\b","message":"Use a stream this time, not a loop."}]}]}