Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Join strings with different last delimiter

Using stream.collect(Collectors.joining(", ")) I can easily join all the strings of my stream delimited by a comma. A possible result would be "a, b, c". But what if I want the last delimiter to be different. For example to be " and " such that I get "a, b and c" as result. Is there an easy solution?

like image 453
principal-ideal-domain Avatar asked Jan 22 '16 00:01

principal-ideal-domain


People also ask

How to concat two strings with comma in Java?

In Java, we can use String. join(",", list) to join a List String with commas.

How to join strings with character Java?

Use StringBuilder : String str; Char a, b, c; a = 'i'; b = 'c'; c = 'e'; StringBuilder sb = new StringBuilder(); sb. append(a); sb. append(b); sb.

How to add comma separated values in StringBuilder in Java?

The simplest way to convert an array to comma separated String is to create a StringBuilder, iterate through the array, and add each element of the array into StringBuilder after appending the comma.


2 Answers

If they are already in a list, no stream is needed; simply join a sublist of all but the last element and concat the other delimiter and the final element:

int last = list.size() - 1;
String joined = String.join(" and ",
                    String.join(", ", list.subList(0, last)),
                    list.get(last));

Here's a version that does the above using Collectors.collectingAndThen:

stream.collect(Collectors.collectingAndThen(Collectors.toList(),
    joiningLastDelimiter(", ", " and ")));

public static Function<List<String>, String> joiningLastDelimiter(
        String delimiter, String lastDelimiter) {
    return list -> {
                int last = list.size() - 1;
                if (last < 1) return String.join(delimiter, list);
                return String.join(lastDelimiter,
                    String.join(delimiter, list.subList(0, last)),
                    list.get(last));
            };
}

This version can also handle the case where the stream is empty or only has one value. Thanks to Holger and Andreas for their suggestions which greatly improved this solution.

I had suggested in a comment that the Oxford comma could be accomplished with this using ", " and ", and" as the delimiters, but that yields incorrect results of "a, and b" for two elements, so just for fun here's one that does Oxford commas correctly:

stream.collect(Collectors.collectingAndThen(Collectors.toList(),
    joiningOxfordComma()));

public static Function<List<String>, String> joiningOxfordComma() {
    return list -> {
                int last = list.size() - 1;
                if (last < 1) return String.join("", list);
                if (last == 1) return String.join(" and ", list);
                return String.join(", and ",
                    String.join(", ", list.subList(0, last)),
                    list.get(last));
            };
}
like image 187
David Conrad Avatar answered Oct 19 '22 22:10

David Conrad


If you're fine with "a, b, and c", then it's possible to use mapLast method of my StreamEx library which extends standard Stream API with additional operations:

String result = StreamEx.of("a", "b", "c")
                        .mapLast("and "::concat)
                        .joining(", "); // "a, b, and c"

The mapLast method applies given mapping to the last stream element keeping others unchanged. I even have similar unit-test.

like image 28
Tagir Valeev Avatar answered Oct 20 '22 00:10

Tagir Valeev