84

I often use this piece of code in PHP

$ordine['address'] = implode(', ', array_filter(array($cliente['cap'], $cliente['citta'], $cliente['provincia'])));

It clears empty strings and join them with a ",". If only one remains it doesn't add an extra unneeded comma. It doesn't add a comma at the end. If none remains it returns empty string.

Thus I can get one of the following results

""
"Street abc 14"
"Street abc 14, 00168"
"Street abc 14, 00168, Rome"

What is the best Java implementation (less code) in Java without having to add external libraries (designing for Android)?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
max4ever
  • 11,909
  • 13
  • 77
  • 115

9 Answers9

101

Updated version using Java 8 (original at the end of post)

If you don't need to filter any elements you can use


Since Java 8 we can use StringJoiner (instead of originally used StringBulder) and simplify our code.
Also to avoid recompiling " *" regex in each call of matches(" *") we can create separate Pattern which will hold its compiled version in some field and use it when needed.

private static final Pattern SPACES_OR_EMPTY = Pattern.compile(" *");
public static String implode(String separator, String... data) {
    StringJoiner sb = new StringJoiner(separator);
    for (String token : data) {
        if (!SPACES_OR_EMPTY.matcher(token).matches()) {
            sb.add(token);
        }
    }
    return sb.toString();
}   

With streams our code can look like.

private static final Predicate<String> IS_NOT_SPACES_ONLY = 
        Pattern.compile("^\\s*$").asPredicate().negate();

public static String implode(String delimiter, String... data) {
    return Arrays.stream(data)
            .filter(IS_NOT_SPACES_ONLY)
            .collect(Collectors.joining(delimiter));
}

If we use streams we can filter elements which Predicate. In this case we want predicate to accept strings which are not only spaces - in other words string must contain non-whitespace character.

We can create such Predicate from Pattern. Predicate created this way will accept any strings which will contain substring which could be matched by regex (so if regex will look for "\\S" predicate will accept strings like "foo ", " foo bar ", "whatever", but will not accept " " nor " ").

So we can use

Pattern.compile("\\S").asPredicate();

or possibly little more descriptive, negation of strings which are only spaces, or empty

Pattern.compile("^\\s*$").asPredicate().negate();

Next when filter will remove all empty, or containing only spaces Strings we can collect rest of elements. Thanks to Collectors.joining we can decide which delimiter to use.


Original answer (before Java 8)

public static String implode(String separator, String... data) {
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < data.length - 1; i++) {
    //data.length - 1 => to not add separator at the end
        if (!data[i].matches(" *")) {//empty string are ""; " "; "  "; and so on
            sb.append(data[i]);
            sb.append(separator);
        }
    }
    sb.append(data[data.length - 1].trim());
    return sb.toString();
}

You can use it like

System.out.println(implode(", ", "ab", " ", "abs"));

or

System.out.println(implode(", ", new String[] { "ab", " ", "abs" }));

Output ab, abs

Pshemo
  • 122,468
  • 25
  • 185
  • 269
  • yeah it's more comfortable to use since you don't have to create an array, and i wasn't aware you can use String , String... – max4ever Jun 28 '12 at 16:17
  • it doesn't work well when data has length 1 xD i use this sb.append(data[0]); for (int i = 1; i < (data.length); i++) { //data.length - 1 => to not add separator at the end if ((data[i] != null) && !data[i].matches(" *")) {//empty string are ""; " "; " "; and so on sb.append(separator); sb.append(data[i]); } } – max4ever Jun 28 '12 at 16:35
  • 1
    @max4ever could you show some example? I tested in on `System.out.println(implode(", ", "X"))` and it seams to work fine. – Pshemo Jun 28 '12 at 16:38
80

Why so serious? Try StringUtils.join(new String[] {"Hello", "World", "!"}, ", ") !

Bogdan Burym
  • 5,482
  • 2
  • 27
  • 46
  • 32
    StringUtils is an external library – max4ever Jan 29 '13 at 15:39
  • 29
    it's made by Apache, everyone uses it... I am doing it Bogdan's way, you guys do what you want – anton1980 Jul 11 '13 at 14:48
  • 10
    It's fine you're "doing it your way", if the library is already available. If it's not, a) you need to know that it IS an external one b) where/how you can get it and c) whether you're allowed to do so. Playing non-corporate cowboy only relieves you from c) – Zefiro May 23 '14 at 17:03
  • 3
    The external library can be found here: http://commons.apache.org/proper/commons-lang/ – Alan B. Dee Aug 01 '14 at 16:01
20

Here is an Android-specific answer that may be helpful to some:

String combined = TextUtils.join(",", new String[]{"Red", "Green", "Blue"});

// Result => Red,Green,Blue

Be sure to import the TextUtils class:

import android.text.TextUtils;
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
dloomb
  • 1,952
  • 22
  • 26
17

You'd have to add your strings to an ArrayList, remove empty ones, and format it accordingly:

public static String createAddressString( String street, String zip_code, String country) {
    List<String> list = new ArrayList<String>();
    list.add( street);
    list.add( zip_code);
    list.add( country);

    // Remove all empty values
    list.removeAll(Arrays.asList("", null));

    // If this list is empty, it only contained blank values
    if( list.isEmpty()) {
        return "";
    }

    // Format the ArrayList as a string, similar to implode
    StringBuilder builder = new StringBuilder();
    builder.append( list.remove(0));

    for( String s : list) {
        builder.append( ", ");
        builder.append( s);
    }

    return builder.toString();
}

Additionally, if you had String[], an array of strings, you can easily add them to an ArrayList:

String[] s;
List<String> list = new ArrayList<String>( Arrays.asList( s));
nickb
  • 59,313
  • 13
  • 108
  • 143
  • In an implementation of implode function, you modify the original list (wrong) and you remove the first element of ArrayList (costy). Also, the suggested transformation from array - why not just asList? Anyway, if the input is a List, the cleanest way to do it yourself is probably using the list's iterator with reading (and appending) the first element pre-cycle, then iterating the rest. Sorry, but I have to give you -1. – Vlasec Jan 12 '15 at 09:59
  • As for the removal of nulls and empty strings, you can also remove them from the output without modifying the original list - but you will of course need a cycle for finding the first non-empty element rather than a simple if. Also, your solution fails on an empty list - `remove(0)` throws an `IndexOutOfBoundsException`. – Vlasec Jan 12 '15 at 10:03
  • @Vlasec - There is no original list, within the method I create my own local list, so your argument is invalid, as the input is not a list, but the three String values. Also, there is a check if the list is empty, which is another invalid argument you've presented. – nickb Jan 12 '15 at 14:51
  • You are technically correct. However, you should still try to teach the best practices here. Some greenhorn might adopt your technique and mess up an existing list. Plus, it's not a particularly effective or elegant approach even if you can destroy the collection. – Vlasec Jan 12 '15 at 17:14
  • @Vlasec - You're entitled to your opinion, however I disagree. Your comment that it's not an effective or elegant solution is based solely on opinion. – nickb Jan 13 '15 at 17:28
3

Using Streams (for Java 8 and later) would be an alternate possible solution for this.

You are required to import

java.util.stream.Collectors;

to use the join process

You may use:

Arrays.asList("foo","bar").stream().collect(Collectors.joining(","));

to achieve the desired result.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Aman J
  • 452
  • 4
  • 15
2

A simple Implode

public static String implode(String glue, String[] strArray)
{
    String ret = "";
    for(int i=0;i<strArray.length;i++)
    {
        ret += (i == strArray.length - 1) ? strArray[i] : strArray[i] + glue;
    }
    return ret;
}

You can create overloads for it..

The above it equivalent of php implode.
Here is what you want:

import java.lang.*
public static String customImplode(String glue, String[] strArray)
{
    String ret = "";
    for(int i=0;i<strArray.length;i++)
    {
        if (strArray[i].trim() != "")
            ret += (i == strArray.length - 1) ? strArray[i] : strArray[i] + glue;
    }
    return ret;
}
Soroush Khosravi
  • 887
  • 2
  • 11
  • 30
1

Here's my implode implementation:

/**
 * Implodes the specified items, gluing them using the specified glue replacing nulls with the specified
 * null placeholder.
 * @param glue              The text to use between the specified items.
 * @param nullPlaceholder   The placeholder to use for items that are <code>null</code> value.
 * @param items             The items to implode.
 * @return  A <code>String</code> containing the items in their order, separated by the specified glue.
 */
public static final String implode(String glue, String nullPlaceholder, String ... items) {
    StringBuilder sb = new StringBuilder();
    for (String item : items) {
        if (item != null) {
            sb.append(item);
        } else {
            sb.append(nullPlaceholder);
        }
        sb.append(glue);
    }
    return sb.delete(sb.length() - glue.length(), sb.length()).toString();
}
Ben Barkay
  • 5,473
  • 2
  • 20
  • 29
1
public static String implode(List<String> items, String separator) {

        if (items == null || items.isEmpty()) {
            return null;
        }
        String delimiter = "";
        StringBuilder builder = new StringBuilder();
        for (String item : items) {
            builder.append(delimiter).append(item);
            delimiter = separator;
        }
        return builder.toString();
    }
Ahmad
  • 189
  • 2
  • 6
0

Use this simple function:

private String my_implode(String spacer, String[] in_array){

    String res = "";

    for (int i = 0 ; i < in_array.length ; i++) {

        if (!res.equals("")) {
            res += spacer;
        }
        res += in_array[i];
    }

    return res;
}

Use:

data_arr = {"d1", "d2", "d3"};
your_imploded_text = my_implode(",", data_arr);
// Output: your_imploded_text = "d1,d2,d3"
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Ferhad Konar
  • 434
  • 1
  • 7
  • 9