Join strings with different last delimiter
If you are looking for old Java solution, using Guava libraries would be easy.
List<String> values = Arrays.asList("a", "b", "c");
String output = Joiner.on(",").join(values);
output = output.substring(0, output.lastIndexOf(","))+" and "+values.get(values.size()-1);
System.out.println(output);//a,b and c
Try joining the last 2 strings first with stream.collect(Collectors.joining(" and "))
Then join all remaining strings and this new string with the code you used in your question: stream.collect(Collectors.joining(", "))
.
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));
};
}
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.