javalambdajava-streamreducecombiners

Reduce a list of string add prefix only to last element using Java 8 Streams


How can i reduce or collect a list of string delimitted by comma and prefixed "and" only to the last element using Java 8 Streams?

eg.

List<String> ls = Arrays.asList("tom","terry","john","kevin","steve");

String result = ls.stream().map(String::toString)
                .collect(Collectors.joining(", "));

System.out.println(result);

This statement prints => tom, terry, john, kevin, steve. But i wanted to print the list as tom, terry, john, kevin and steve.


Solution

  • Two solutions.

    Clumsy, taking sublist actually:

        String result = ls.stream()
                .limit(ls.size() - 1)
                .collect(Collectors.joining(", ")) + " and " + ls.get(ls.size() - 1);
    

    Using a class to shift one place:

        class SbS {
            StringBuilder sb = new StringBuilder();
            String s = "";
    
            @Override
            public String toString() {
                return sb + (s.empty() ? "" : " and " + s);
            }
        }
        result = ls.stream()
                .collect(SbS::new,
                    (a, s) -> {
                        a.sb.append(a.sb.length() == 0 ? "" : ", ").append(a.s); a.s = s;
                    },
                    (a1, a2) -> {}).toString();
    

    Mind: the class definition should be placed inside the method, just as above. Otherwise static would be needed. The lambda for the (a, s) could be a method in SbS. The support for parallelism is left as excercise to the reader (a1, a1).