How to create a text string with separators efficiently?

This question is to share a very simple trick I learned in StackOverflow that has served to clean up thousands of cases in my code.

Note: is not a translation, it is simply a transmission of knowledge that I believe necessary and interesting and originally created by me for SO in Spanish.


We have all assembled a text string by hand by inserting the separators, by example:

  • Features of an element (car) separated by comma ,

    ABS, ESP, EE, CC
    
  • Printable list with line breaks \n

    producto1   2,23\n
    producto2   3,23\n
    producto33  5,31\n
    

And we have encountered one of the following problems:

  • Insert a comparison to each iteration:

    JAVA

    // bucle que inserta valor v en variable x
    if ("".equals(x)) x = v;
    else              x = "," + v;
    

    JAVASCRIPT

    // bucle que inserta valor v en variable x
    if (x == "") x = v;
    else         x = "," + v;
    
  • If we do not insert that comparison to optimize, equally the we have to perform later to avoid

    • Last empty element:

      1,1,1,1,1,
      //       ↑ aquí!
      
    • First empty element

        ,1,1,1,1,1
      //↑ aquí!
      

Question

Is there a pattern to avoid this usual and annoying case and that meets the following characteristics?

  • cross (usable in any language).
  • optimal (avoid expensive functions/ methods, comparisons or iterations extras)
  • readable
 6
Author: Jordi Castilla, 2015-12-03

6 answers

This is another way of dealing with it.

In cases where you work with items, and you add them, the ideal is to use a array.

And from there:

  1. array.push() adds an element in the array
  2. array.join() concatenates all elements with a separator

Code

var separador = ',';
var items = []; //array vacío

for (var i = 0; i < 10; i++) {
    // agrega 1 elemento al array
    items.push(i);
}

//concatena con separador
var arrayManual = items.join(separador)

// mostrar el resultado
resultado.innerText = arrayManual;
<pre id="resultado"></pre>
 5
Author: Mariano, 2015-12-03 11:51:21

There is really a very simple and effective trick that makes the strings created in this way to assemble perfectly optimizing the code to the maximum:

  • create a variable SEPARADOR empty at the beginning and that you assign at the end of each iteration:

JAVA WITHOUT USING THE TECHNIQUE

final String SEPARADOR = ",";
StringBuilder cadena = new StringBuilder("");
for (String s : listaDeStrings) {   
    if (cadena.equals("")) {
        cadena.append(s);
    } else {
        cadena.append(SEPARADOR);
        cadena.append(s);
    }
}

JAVA

String SEPARADOR = "";
StringBuilder cadena= new StringBuilder();
for (String s : listaDeStrings) {   
    cadena.append(SEPARADOR);
    cadena.append(s);
    SEPARADOR = ",";
}

JAVASCRIPT / JQUERY WITHOUT USING TECHNIQUE

var separador = ",";
var cadena= "";

$('.datos').each(function(index, value) {
    if (cadena === "") {
        cadena += separador + value.value;
    } else {
        cadena += separador + value.value;
        separador = ",";
    }
});

JAVASCRIPT / JQUERY

var separador = "";
var cadena = "";

$('.datos').each(function(index, value) {
    cadena += separador + value.value;
    separador = ",";
});

Ahi it remains for anyone who wants to use it.

  • greatly visually improves clarity and readability
  • greatly improves performance
  • facilitates data integrity.
 5
Author: Jordi Castilla, 2015-12-03 12:48:22

There is no optimal method for all languages. Even within the same language, the optimal method may vary depending on the data structure where the data to concatenate is.

For 99% of programs, making code readable is more important than optimizing it. Make it readable first, and then optimize only if you verify that it is necessary.

  • If the language has any function that does this, use it. It is the most readable and usually the most optimal. For example, in javascript it uses Array.join if the data is in an array.

  • If not, just make it readable and don't worry about optimizing it for now. A if in the loop is readable, an empty variable that is updated in the loop as Jordi Castilla proposes in his answer as well. To prefer one method or another depends on each programmer.

If after implementing it you check that you need to improve performance, one possible option, if the way of traversing the data allows, is to take the first iteration out of the loop. For example:

List<String> listaConDatos = obtenerDatos();
StringBuffer resultado = new StringBuffer();
String separador = ",";

Iterator<String> it = listaConDatos.iterator();
if (it.hasNext()) {
  resultado.append(it.next());
  while (it.hasNext()) {
    resultado.append(separador);
    resultado.append(it.next());
  }
}

The problem is that this is clearly less readable, and not all ways of traversing the data allow it to be done.

 5
Author: Pablo, 2015-12-03 14:49:39

The other answers compare to an empty string. I don't understand if that's part of the problem definition. If it is not, the following is my solution.

I usually use a variable boolean. Something like:

boolean primerElemento = false;
for (/* ... */) {
   if (primerElemento) {
      primerElemento = false;
   } else {
      buffer.append(",");
   }
   buffer.append(siguenteElemento);
}
 3
Author: ArturoTena, 2015-12-19 02:22:48

Too many turns. The least expensive thing is to trim the comma at the end.

That's all, the larger the amount of data any 1-to-1 validation becomes too expensive, and it is precisely in that case when it needs to be optimal.

Clears the end comma. A single operation only once per row. End

Now that's the least of the problems some of the java and js demos that have shown you inefficiency is the rule... You can do things much more optimal with C # or C, C++... But well, you asked for something agnostic. There you have it .

 2
Author: JuanK, 2015-12-19 04:26:35

In JAVA 8 we can do it like this:

public static void main(String[] args) {
    String[] array = {"Hola", "David"};
    System.out.println(concat(array));
}
public static String concat(String... strs) {
    StringJoiner joiner = new StringJoiner(", ");
    for (String str : strs) {
        joiner.add(str);
    }
    return joiner.toString();
}

The result will be: Hello, David

Notice that the result does not include any', ' behind David.

 0
Author: davidddp, 2018-08-29 07:19:33