Joining a List<String> in Java with commas and "and"

JavaJoinApache Commons

Java Problem Overview


Given a list

List<String> l = new ArrayList<String>();
l.add("one");
l.add("two");
l.add("three");

I have a method

String join(List<String> messages) {
		if (messages.isEmpty()) return "";
		if (messages.size() == 1) return messages.get(0);
		String message = "";
		message = StringUtils.join(messages.subList(0, messages.size() -2), ", ");
		message = message + (messages.size() > 2 ? ", " : "") + StringUtils.join(messages.subList(messages.size() -2, messages.size()), ", and ");
		return message;
	}

which, for l, produces "one, two, and three". My question is, is there a standard (apache-commons) method that does the same?, eg

WhatEverUtils.join(l, ", ", ", and ");

To clarify. My problem is not getting this method to work. It works just as I want it to, it's tested and all is well. My problem is that I could not find some apache-commons-like module which implements such functionality. Which surprises me, since I cannot be the first one to need this.

But then maybe everyone else has just done

StringUtils.join(l, ", ").replaceAll(lastCommaRegex, ", and");

Java Solutions


Solution 1 - Java

In Java 8 you can use String.join() like following:

Collection<String> elements = ....;
String result = String.join(", ", elements);

Solution 2 - Java

I like using Guava for this purpose. Neat and very useful:

Joiner.on(",").join(myList)

This kind of code has been written time and time again and you should rather be freed implementing your specific implementation logic.

If you use maven, herewith the dependency:

<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>28.1-jre</version>
</dependency>

It has a bunch of other wonderful cool features too!

This will produce the string "one, two, and three".

List<String> originalList = Arrays.asList("one", "two", "three");
Joiner.on(", ")
    .join(originalList.subList(0, originalList.size() - 1))
    .concat(", and ")
    .concat(originalList.get(originalList.size() - 1));

Solution 3 - Java

What about join from: org.apache.commons.lang.StringUtils

Example:

StringUtils.join(new String[] { "one", "two", "three" }, ", "); // one, two, three

To have "and" or ", and" you can simple replace the last comma.

Solution 4 - Java

With Java 8, you can use streams with joiners.

Collection<String> strings;
...
String commaDelimited = strings.stream().collect(Collectors.joining(","));
// use strings.parallelStream() instead, if you think
//   there are gains to be had by doing fork/join

Solution 5 - Java

To produce grammatical output in English there are 3 cases to consider when concatenating a list of strings:

  1. "A"

  2. "A and B"

  3. "A, B, and C.

This can be accomplished using standard Java or Guava like below. The solutions are basically the same and just up to preference what you want to use.

import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;

import org.junit.Test;

import java.util.List;

import static org.junit.Assert.assertEquals;

public class JoinListTest {

    @Test
    public void test_join() {
        // create cases (don't need to use ImmutableList builder from guava)
        final List<String> case1 = new ImmutableList.Builder<String>().add("A").build();
        final List<String> case2 = new ImmutableList.Builder<String>().add("A", "B").build();
        final List<String> case3 = new ImmutableList.Builder<String>().add("A", "B", "C").build();
        // test with standard java
        assertEquals("A", joinListGrammaticallyWithJava(case1));
        assertEquals("A and B", joinListGrammaticallyWithJava(case2));
        assertEquals("A, B, and C", joinListGrammaticallyWithJava(case3));
        // test with guava
        assertEquals("A", joinListGrammaticallyWithGuava(case1));
        assertEquals("A and B", joinListGrammaticallyWithGuava(case2));
        assertEquals("A, B, and C", joinListGrammaticallyWithGuava(case3));
    }

    private String joinListGrammaticallyWithJava(final List<String> list) {
        return list.size() > 1
                ? String.join(", ", list.subList(0, list.size() - 1))
                    .concat(String.format("%s and ", list.size() > 2 ? "," : ""))
                    .concat(list.get(list.size() - 1))
                : list.get(0);
    }

    private String joinListGrammaticallyWithGuava(final List<String> list) {
        return list.size() > 1
                ? Joiner.on(", ").join(list.subList(0, list.size() - 1))
                    .concat(String.format("%s and ", list.size() > 2 ? "," : ""))
                    .concat(list.get(list.size() - 1))
                : list.get(0);
    }

}

Solution 6 - Java

Other answers talk about "replacing the last comma", which isn't safe in case the last term itself contains a comma.

Rather than use a library, you can just use one (albeit long) line of JDK code:

public static String join(List<String> msgs) {
    return msgs == null || msgs.size() == 0 ? "" : msgs.size() == 1 ? msgs.get(0) : msgs.subList(0, msgs.size() - 1).toString().replaceAll("^.|.$", "") + " and " + msgs.get(msgs.size() - 1);
}

See a live demo of this code handling all edge cases.


FYI, here's a more readable two-liner:

public static String join(List<String> msgs) {
	int size = msgs == null ? 0 : msgs.size();
    return size == 0 ? "" : size == 1 ? msgs.get(0) : msgs.subList(0, --size).toString().replaceAll("^.|.$", "") + " and " + msgs.get(size);
}

Solution 7 - Java

I don't know any Apache String joiner that can support adding and in the joined String.

Here's an untested code that will do what you asked:

public static String join(String separator, List<String> mList, boolean includeAndInText) {
	StringBuilder sb = new StringBuilder();
	int count = 0;
	
	for (String m: mList) {
		if (includeAndInText && (count + 1 != mList.size())) {
			sb.append (" and ");
		}
		
		sb.append(m);
		count++;
		if (count < mList.size()) {
			sp.append(separator);
		}		
	}
	
	return sb.toString();
}

Solution 8 - Java

Improved version from Bohemian♦'s answer. You can choose to remove the nulled items check on personal preferences.

/** Auto Concat Wrapper
 *  Wraps a list of string with comma and concat the last element with "and" string.
 *  E.g: List["A", "B", "C", "D"] -> Output: "A, B, C and D"
 * @param elements
 */
public static String join(List<String> elements){
    if(elements==null){return "";}
    List<String> tmp = new ArrayList<>(elements);
    tmp.removeAll(Collections.singleton(null)); //Remove all nulled items
    
    int size = tmp.size();
    return size == 0 ? "" : size == 1 ? tmp.get(0) : String.join(", ", tmp.subList(0, --size)).concat(" and ").concat(tmp.get(size));
}

Test results:

List<String> w = Arrays.asList("A");
List<String> x = Arrays.asList("A", "B");
List<String> y = Arrays.asList("A", "B", null, "C");
List<String> z = Arrays.asList("A", "B", "C", "D");
System.out.println(join(w));//A
System.out.println(join(x));//A and B
System.out.println(join(y));//A, B and C
System.out.println(join(z));//A, B, C and D

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionslipsetView Question on Stackoverflow
Solution 1 - JavaAli DehghaniView Answer on Stackoverflow
Solution 2 - JavaJaco Van NiekerkView Answer on Stackoverflow
Solution 3 - JavalukastymoView Answer on Stackoverflow
Solution 4 - JavaCppNoobView Answer on Stackoverflow
Solution 5 - JavaAbtin GramianView Answer on Stackoverflow
Solution 6 - JavaBohemianView Answer on Stackoverflow
Solution 7 - JavaBuhake SindiView Answer on Stackoverflow
Solution 8 - JavaSuper ElephantView Answer on Stackoverflow