{"id":"useful-techniques-end-of-part-10","slug":"end-of-part-10","title":"Putting it together, and the end of part 10","text":"This last step of part 10 puts this module's tools side by side, and then looks back at the whole part.\n\n### Which tool when\n\n| You want to | Use | Instead of |\n| --- | --- | --- |\n| count down, or step by more than one | `for (int i = 10; i > 0; i -= 2)` | a counter in a `while` loop |\n| use the positions of a list's values | `for` over the indexes | a for-each loop |\n| build a text in a loop | `StringBuilder` and `append` | `+=` on a String |\n| check the form of a string | `matches` and a regular expression | a loop with `charAt` |\n| a value from a fixed set | an enum, compared with `==` | Strings or numbers that stand for the choices |\n| remove elements while going through a collection | an `Iterator` and its `remove()` | removing inside a for-each loop |\n\nThe tools work well together. A regex check that a code has the right form can decide which enum constant the code stands for, a StringBuilder can build a report with a line for each constant in `values()`, and an iterator can clear out the objects that a report no longer needs. The challenges of this step do all of that, for the tickets of a music festival.\n\n### The end of part 10\n\nThis step ends the material of the MOOC's tenth part. Module 37 brought streams: `stream()` and a chain of steps such as `filter`, `map`, `sorted` and `distinct`, ended by `count`, `sum`, `average`, `collect` or `forEach`, with lambda expressions and method references to say what each step does, and `reduce`, `IntStream` and the lines of a file as a stream. Module 38 put objects in order: a natural order with `Comparable` and `compareTo`, and any other order with a `Comparator`, built with `Comparator.comparing`, `thenComparing` and `reversed`. This module added a set of smaller tools: the for loop once more, StringBuilder, regular expressions, enums with fields and methods, and iterators. Part 11 comes next, starting with class diagrams. Part 10 ends with [the MOOC's summary page](https://java-programming.mooc.fi/part-10/4-summary) on its own site.\n","task":"A music festival sells three kinds of passes. `Pass.java` is ready, with a price for each. A ticket's code is the first letter of its pass's name, a dash and four digits: `D-0412` is a day pass, `W-1001` a weekend pass and `B-0007` a backstage pass. In `Main.java`, write the method `passFor(String code)`, which returns the Pass that a code stands for, or `null` when the code doesn't have that form. Check the form with `matches`, and find the pass by going through `Pass.values()`. The check runs:\n\n```java\nSystem.out.println(passFor(\"W-1001\"));\nSystem.out.println(passFor(\"B-0007\"));\nSystem.out.println(passFor(\"D-412\"));\nSystem.out.println(passFor(\"X-1234\"));\n```\n\nand expects:\n\n```\nWEEKEND\nBACKSTAGE\nnull\nnull\n```\n","kind":"code","seed":"// ==== Main.java ====\npublic class Main {\n    public static void main(String[] args) {\n        System.out.println(passFor(\"W-1001\"));\n        System.out.println(passFor(\"B-0007\"));\n        System.out.println(passFor(\"D-412\"));\n        System.out.println(passFor(\"X-1234\"));\n    }\n\n    public static Pass passFor(String code) {\n        return null;\n    }\n}\n\n// ==== Pass.java ====\npublic enum Pass {\n    DAY(45),\n    WEEKEND(110),\n    BACKSTAGE(250);\n\n    private int price;\n\n    private Pass(int price) {\n        this.price = price;\n    }\n\n    public int getPrice() {\n        return this.price;\n    }\n}\n","solution":"// ==== Main.java ====\npublic class Main {\n    public static void main(String[] args) {\n        System.out.println(passFor(\"W-1001\"));\n        System.out.println(passFor(\"B-0007\"));\n        System.out.println(passFor(\"D-412\"));\n        System.out.println(passFor(\"X-1234\"));\n    }\n\n    public static Pass passFor(String code) {\n        if (!code.matches(\"[DWB]-[0-9]{4}\")) {\n            return null;\n        }\n        for (Pass pass : Pass.values()) {\n            if (pass.name().charAt(0) == code.charAt(0)) {\n                return pass;\n            }\n        }\n        return null;\n    }\n}\n\n// ==== Pass.java ====\npublic enum Pass {\n    DAY(45),\n    WEEKEND(110),\n    BACKSTAGE(250);\n\n    private int price;\n\n    private Pass(int price) {\n        this.price = price;\n    }\n\n    public int getPrice() {\n        return this.price;\n    }\n}\n","hints":["The form is one of the letters D, W or B, a dash, and four digits: `[DWB]-[0-9]{4}`. When the code doesn't match, return `null` at once.","Then go through `Pass.values()`, and return the pass whose `name().charAt(0)` equals the code's `charAt(0)`. Both are chars, so compare them with `==`."],"tests":[{"name":"Test 1","stdin":"","call":"System.out.println(passFor(\"W-1001\"));\nSystem.out.println(passFor(\"B-0007\"));\nSystem.out.println(passFor(\"D-412\"));\nSystem.out.println(passFor(\"X-1234\"));\n","expect":"WEEKEND\nBACKSTAGE\nnull\nnull","hidden":false},{"name":"Test 2","stdin":"","call":"System.out.println(passFor(\"D-0412\") + \" \" + passFor(\"D-0000\") + \" \" + (passFor(\"B-9999\") == Pass.BACKSTAGE));\nSystem.out.println(passFor(\"d-0412\") + \" \" + passFor(\"W-10010\") + \" \" + passFor(\"W1001\") + \" \" + passFor(\"WD-1001\") + \" \" + passFor(\"\"));\n","expect":"DAY DAY true\nnull null null null null","hidden":true},{"name":"Test 3","stdin":"","call":"Pass pass = passFor(\"W-2026\");\nSystem.out.println(pass.getPrice() + \" \" + passFor(\"B-12a4\") + \" \" + passFor(\" D-1234\"));\n","expect":"110 null null","hidden":true}],"require":[{"pattern":"\\.matches\\s*\\(","message":"Check the code's form with `matches`."},{"pattern":"\\bPass\\s*\\.\\s*values\\s*\\(\\s*\\)","message":"Find the pass by going through `Pass.values()`."}],"forbid":[],"more":[{"task":"The festival's passes once more. What does this program print?\n","kind":"predict","seed":"public class Main {\n    public static void main(String[] args) {\n        StringBuilder line = new StringBuilder();\n        for (Pass pass : Pass.values()) {\n            line.append(pass.name().charAt(0));\n        }\n        System.out.println(line.reverse());\n        String code = line + \"-0042\";\n        System.out.println(code.matches(\"[A-Z]+-[0-9]{4}\") + \" \" + code.matches(\"[DWB]-[0-9]+\"));\n        int total = 0;\n        for (int i = Pass.values().length - 1; i >= 0; i -= 2) {\n            total += Pass.values()[i].getPrice();\n        }\n        System.out.println(total);\n    }\n}\n// ==== Pass.java ====\npublic enum Pass {\n    DAY(45),\n    WEEKEND(110),\n    BACKSTAGE(250);\n\n    private int price;\n\n    private Pass(int price) {\n        this.price = price;\n    }\n\n    public int getPrice() {\n        return this.price;\n    }\n}\n","solution":"public class Main {\n    public static void main(String[] args) {\n        StringBuilder line = new StringBuilder();\n        for (Pass pass : Pass.values()) {\n            line.append(pass.name().charAt(0));\n        }\n        System.out.println(line.reverse());\n        String code = line + \"-0042\";\n        System.out.println(code.matches(\"[A-Z]+-[0-9]{4}\") + \" \" + code.matches(\"[DWB]-[0-9]+\"));\n        int total = 0;\n        for (int i = Pass.values().length - 1; i >= 0; i -= 2) {\n            total += Pass.values()[i].getPrice();\n        }\n        System.out.println(total);\n    }\n}\n// ==== Pass.java ====\npublic enum Pass {\n    DAY(45),\n    WEEKEND(110),\n    BACKSTAGE(250);\n\n    private int price;\n\n    private Pass(int price) {\n        this.price = price;\n    }\n\n    public int getPrice() {\n        return this.price;\n    }\n}\n","hints":["The loop appends the first letters of DAY, WEEKEND and BACKSTAGE. `reverse()` turns the builder itself around and returns it, so it's printed reversed, and it stays reversed for the next line.","The code is the three letters, a dash and 0042: `[A-Z]+` allows any number of capital letters, but `[DWB]` is exactly one. The last loop takes indexes 2 and 0, BACKSTAGE and DAY."],"tests":[{"name":"Output","stdin":"","expect":"BWD\ntrue false\n295","hidden":false}],"require":[],"forbid":[],"lines":["BWD","true false","295"]},{"task":"To end part 10: the festival's box office. `Pass.java` is the one from the first task, and `Ticket.java` is ready: a ticket has the name of its holder and a Pass. One person can hold several tickets. In `BoxOffice.java`, `sell` is ready. Write:\n\n- `refund(String holder)`, for a person who can't come after all: it removes every ticket of that holder, with an iterator, and returns the money that goes back, the sum of the prices of the removed tickets' passes (0 when the holder has no tickets)\n- `report()`, which returns a String built with a StringBuilder: one line for each pass, in the order of `Pass.values()`, like `DAY: 3 sold, 135 dollars`, with a line break between the lines and none after the last\n\n`Main.java` is a test drive, and it prints:\n\n```\nDAY: 3 sold, 135 dollars\nWEEKEND: 1 sold, 110 dollars\nBACKSTAGE: 1 sold, 250 dollars\nRefunded: 405\nDAY: 2 sold, 90 dollars\nWEEKEND: 0 sold, 0 dollars\nBACKSTAGE: 0 sold, 0 dollars\n```\n","kind":"code","seed":"// ==== BoxOffice.java ====\nimport java.util.ArrayList;\nimport java.util.Iterator;\n\npublic class BoxOffice {\n    private ArrayList<Ticket> tickets;\n\n    public BoxOffice() {\n        this.tickets = new ArrayList<>();\n    }\n\n    public void sell(String holder, Pass pass) {\n        this.tickets.add(new Ticket(holder, pass));\n    }\n\n    public int refund(String holder) {\n        return 0;\n    }\n\n    public String report() {\n        return \"\";\n    }\n}\n\n// ==== Ticket.java ====\npublic class Ticket {\n    private String holder;\n    private Pass pass;\n\n    public Ticket(String holder, Pass pass) {\n        this.holder = holder;\n        this.pass = pass;\n    }\n\n    public String getHolder() {\n        return this.holder;\n    }\n\n    public Pass getPass() {\n        return this.pass;\n    }\n\n    @Override\n    public String toString() {\n        return this.holder + \" (\" + this.pass + \")\";\n    }\n}\n\n// ==== Pass.java ====\npublic enum Pass {\n    DAY(45),\n    WEEKEND(110),\n    BACKSTAGE(250);\n\n    private int price;\n\n    private Pass(int price) {\n        this.price = price;\n    }\n\n    public int getPrice() {\n        return this.price;\n    }\n}\n\n// ==== Main.java ====\npublic class Main {\n    public static void main(String[] args) {\n        BoxOffice office = new BoxOffice();\n        office.sell(\"Ada\", Pass.DAY);\n        office.sell(\"Ben\", Pass.WEEKEND);\n        office.sell(\"Ben\", Pass.BACKSTAGE);\n        office.sell(\"Cy\", Pass.DAY);\n        office.sell(\"Ben\", Pass.DAY);\n        System.out.println(office.report());\n        System.out.println(\"Refunded: \" + office.refund(\"Ben\"));\n        System.out.println(office.report());\n    }\n}\n","solution":"// ==== BoxOffice.java ====\nimport java.util.ArrayList;\nimport java.util.Iterator;\n\npublic class BoxOffice {\n    private ArrayList<Ticket> tickets;\n\n    public BoxOffice() {\n        this.tickets = new ArrayList<>();\n    }\n\n    public void sell(String holder, Pass pass) {\n        this.tickets.add(new Ticket(holder, pass));\n    }\n\n    public int refund(String holder) {\n        int money = 0;\n        Iterator<Ticket> iterator = this.tickets.iterator();\n        while (iterator.hasNext()) {\n            Ticket ticket = iterator.next();\n            if (ticket.getHolder().equals(holder)) {\n                iterator.remove();\n                money += ticket.getPass().getPrice();\n            }\n        }\n        return money;\n    }\n\n    public String report() {\n        StringBuilder report = new StringBuilder();\n        for (Pass pass : Pass.values()) {\n            int sold = 0;\n            for (Ticket ticket : this.tickets) {\n                if (ticket.getPass() == pass) {\n                    sold++;\n                }\n            }\n            if (report.length() > 0) {\n                report.append(\"\\n\");\n            }\n            report.append(pass).append(\": \").append(sold).append(\" sold, \").append(sold * pass.getPrice()).append(\" dollars\");\n        }\n        return report.toString();\n    }\n}\n\n// ==== Ticket.java ====\npublic class Ticket {\n    private String holder;\n    private Pass pass;\n\n    public Ticket(String holder, Pass pass) {\n        this.holder = holder;\n        this.pass = pass;\n    }\n\n    public String getHolder() {\n        return this.holder;\n    }\n\n    public Pass getPass() {\n        return this.pass;\n    }\n\n    @Override\n    public String toString() {\n        return this.holder + \" (\" + this.pass + \")\";\n    }\n}\n\n// ==== Pass.java ====\npublic enum Pass {\n    DAY(45),\n    WEEKEND(110),\n    BACKSTAGE(250);\n\n    private int price;\n\n    private Pass(int price) {\n        this.price = price;\n    }\n\n    public int getPrice() {\n        return this.price;\n    }\n}\n\n// ==== Main.java ====\npublic class Main {\n    public static void main(String[] args) {\n        BoxOffice office = new BoxOffice();\n        office.sell(\"Ada\", Pass.DAY);\n        office.sell(\"Ben\", Pass.WEEKEND);\n        office.sell(\"Ben\", Pass.BACKSTAGE);\n        office.sell(\"Cy\", Pass.DAY);\n        office.sell(\"Ben\", Pass.DAY);\n        System.out.println(office.report());\n        System.out.println(\"Refunded: \" + office.refund(\"Ben\"));\n        System.out.println(office.report());\n    }\n}\n","hints":["`refund` is step 7's loop: an iterator over `this.tickets`. Keep the ticket from `next()` in a variable, since you need both its holder and its pass. When the holder `equals` the name asked for, call `iterator.remove()` and add `ticket.getPass().getPrice()` to the money. Return the money after the loop.","`report` makes one StringBuilder, and for each pass in `Pass.values()` counts the tickets of that pass with a for-each loop. Before every line but the first (when `report.length() > 0`), append `\"\\n\"`; then append the pass, the count and `count * pass.getPrice()` with the words in between."],"tests":[{"name":"The test drive","stdin":"","expect":"DAY: 3 sold, 135 dollars\nWEEKEND: 1 sold, 110 dollars\nBACKSTAGE: 1 sold, 250 dollars\nRefunded: 405\nDAY: 2 sold, 90 dollars\nWEEKEND: 0 sold, 0 dollars\nBACKSTAGE: 0 sold, 0 dollars","hidden":false},{"name":"Test 2","stdin":"","call":"BoxOffice office = new BoxOffice();\nSystem.out.println(office.report());\nSystem.out.println(office.refund(\"Kai\"));\noffice.sell(\"Kai\", Pass.BACKSTAGE);\noffice.sell(\"Lin\", Pass.BACKSTAGE);\noffice.sell(\"Kai\", Pass.WEEKEND);\noffice.sell(\"Mo\", Pass.DAY);\nSystem.out.println(office.refund(\"Zed\") + \" \" + office.refund(\"Kai\") + \" \" + office.refund(\"Kai\"));\nSystem.out.println(office.report());\n","expect":"DAY: 0 sold, 0 dollars\nWEEKEND: 0 sold, 0 dollars\nBACKSTAGE: 0 sold, 0 dollars\n0\n0 360 0\nDAY: 1 sold, 45 dollars\nWEEKEND: 0 sold, 0 dollars\nBACKSTAGE: 1 sold, 250 dollars","hidden":true},{"name":"Test 3","stdin":"","call":"BoxOffice office = new BoxOffice();\nfor (int i = 0; i < 10; i++) {\n    office.sell(\"Fan \" + (i % 4), Pass.values()[i % 3]);\n}\nSystem.out.println(office.report());\nSystem.out.println(office.refund(\"Fan 2\") + \" \" + office.refund(\"Fan 2\"));\noffice.sell(\"Late\", Pass.WEEKEND);\nSystem.out.println(office.report());\n","expect":"DAY: 4 sold, 180 dollars\nWEEKEND: 3 sold, 330 dollars\nBACKSTAGE: 3 sold, 750 dollars\n295 0\nDAY: 3 sold, 135 dollars\nWEEKEND: 4 sold, 440 dollars\nBACKSTAGE: 2 sold, 500 dollars","hidden":true}],"require":[{"pattern":"\\.iterator\\s*\\(\\s*\\)","message":"Remove the refunded tickets with an iterator: `this.tickets.iterator()`."},{"pattern":"\\bnew\\s+StringBuilder\\s*\\(","message":"Build the report in a `StringBuilder`."},{"pattern":"\\bPass\\s*\\.\\s*values\\s*\\(\\s*\\)","message":"Go through the passes with `Pass.values()`, so the report has a line for each one, in the enum's order."}],"forbid":[{"pattern":"\\bremoveIf\\b","message":"`removeIf` would do the loop for you. Use an iterator this time."},{"pattern":"\\bclass\\s+BoxOffice\\b[\\s\\S]*\\bSystem\\s*\\.\\s*out\\b[\\s\\S]*\\bclass\\s+Ticket\\b","message":"BoxOffice doesn't print. `report` returns the text, and `main` prints it."}]}]}