Attach strings with different last delimiter

Using stream.collect(Collectors.joining(", ")) , I can easily concatenate all the lines of my stream, separated by commas. 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 the result. Is there a simple solution?

+7
java string java-8 java-stream
source share
6 answers

If they are already in the list, a stream is not required; just attach a sublist of everything except the last element, and separate the other separator and the final element:

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

Here is the version that does this 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 when the stream is empty or has only one value. Thanks to Holger and Andreas for their suggestions that greatly improved this solution.

I suggested in a comment that the Oxford comma can be executed using ", " and ", and" as delimiters, but this leads to incorrect "a, and b" results for the two elements, so just for fun here, Oxford writes 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)); }; } 
+4
source share

If you are fine with "a, b, and c" , then you can use the mapLast method of my StreamEx , which extends the 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 this mapping to the last element of the stream, keeping the others unchanged. I even have a similar unit-test .

+4
source share

Try joining the last 2 lines first with stream.collect(Collectors.joining(" and "))

Then append all the other lines and this new line with the code that you used in your question: stream.collect(Collectors.joining(", ")) .

+1
source share

If you are looking for an 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 
+1
source share
  List<String> names = Arrays.asList("Thomas", "Pierre", "Yussef", "Rick"); int length = names.size(); String result = IntStream.range(0, length - 1).mapToObj(i -> { if (i == length - 2) { return names.get(i) + " and " + names.get(length - 1); } else { return names.get(i); } }).collect(Collectors.joining(", ")); 
0
source share
 String str = "a , b , c , d"; String what_you_want = str.substring(0, str.lastIndexOf(",")) + str.substring(str.lastIndexOf(",")).replace(",", "and"); // what_you_want is : a , b , c and d 
-one
source share

All Articles