{"id":"code-dev-java-loop-to-stream-keep-order","audience":"developers","category":"coding","track":"coding","technique":"constraints","title":"Convert a Java loop to a stream without losing key order","free":false,"task":"Convert idsByCustomer to a single stream pipeline. The invoice printer relies on customers appearing in first-seen order, so the result must stay a LinkedHashMap. Also, from now on customer ids must be trimmed and upper-cased (the new import has values like ' acme '). Skip VOID orders as before, and now also skip test orders, which have cents of 0. Keep the signature. Return only the method in exactly 1 code block of at most 10 lines, with under 15 words of text.","materialLabel":"The method and where it's used (attached to your prompt automatically)","material":"// InvoiceGrouping.java  (Java 17, billing-service)\nimport java.util.*;\nimport java.util.stream.*;\n\npublic final class InvoiceGrouping {\n\n    public record Order(String id, String customerId, String status, long cents) {}\n\n    /** Groups order ids by customer, skipping voided orders. */\n    public static Map<String, List<String>> idsByCustomer(List<Order> orders) {\n        Map<String, List<String>> result = new LinkedHashMap<>();\n        for (Order o : orders) {\n            if (\"VOID\".equals(o.status())) {\n                continue;\n            }\n            result.computeIfAbsent(o.customerId(), k -> new ArrayList<>()).add(o.id());\n        }\n        return result;\n    }\n}\n\n// InvoicePrinter.java (excerpt)\n// for (var entry : InvoiceGrouping.idsByCustomer(orders).entrySet()) {\n//     printHeader(entry.getKey());\n//     entry.getValue().forEach(this::printLine);\n// }\n\n// Sample input (from the October import):\n//   new Order(\"A-1\", \"acme\",   \"PAID\", 1200)\n//   new Order(\"A-2\", \"globex\", \"VOID\", 500)\n//   new Order(\"A-3\", \" acme \", \"PAID\", 800)\n//   new Order(\"A-4\", \"Initech\",\"PAID\", 300)\n// Current output: {acme=[A-1], \" acme \"=[A-3], Initech=[A-4]}","rules":[{"type":"codeBlocks","count":1},{"type":"maxCodeLines","max":10},{"type":"maxProseWords","max":15}],"mistakes":[{"title":"Losing the order","why":"A plain groupingBy returns a HashMap, so the invoice index comes out shuffled.","fix":"Add: \"Keep first-seen order: groupingBy(key, LinkedHashMap::new, mapping(...)).\"","checks":["r0"]},{"title":"Not mentioning the new rule","why":"The AI only converts what's there; it can't know ids must now be cleaned.","fix":"Add: \"Group by customerId().trim().toUpperCase().\"","checks":["r1"]},{"title":"Missing the test-order filter","why":"The AI only keeps the VOID check it can see; it can't know 0-cent test orders must go.","fix":"Add: \"Also skip orders with cents == 0. Keep the signature; one return statement.\"","checks":["r2"]},{"title":"The whole class and a lecture","why":"The AI reprints the class and explains streams.","fix":"Add: \"Only the method, one code block, no text.\"","checks":["codeBlocks","maxCodeLines","maxProseWords"]}],"promptHints":[{"label":"Protects the order","patterns":["order","LinkedHashMap"]},{"label":"Gives the new id rule","patterns":["trim","upper"]},{"label":"Adds the test-order filter","patterns":["cents","test order","\\b0\\b"]},{"label":"Protects the signature","patterns":["signature","same (name|return)","keep.{0,20}(method|signature)"]},{"label":"Asks for only the method","patterns":["only the method","just the method","no (text|explanation)"]}],"hint":"A stream that looks the same can still change the output. Tell the AI what order the caller depends on, the new rules, and what must stay.","rubricLabels":["Uses Collectors.groupingBy with a LinkedHashMap::new map factory (three-argument form), so customers stay in first-seen order; a plain two-argument groupingBy fails","Groups by the trimmed, upper-cased customer id (o.customerId().trim().toUpperCase()), so 'acme' and ' acme ' end up under ACME","Skips both VOID orders and orders with cents == 0, keeps the signature public static Map<String, List<String>> idsByCustomer(List<Order> orders), and the body is one return statement with no for loop"]}