Fastest way to aggregate a character list

The fastest way to aggregate a list of character values when all values are non null is to *always* add the delimiter *before* the next element inside the loop and remove it afterward with cList = SUBSTRING(cList, LENGTH(cDelimiter) + 1) (method 4).

If the delimiter is fixed, we can of course use its fixed length instead of LENGTH(cDelimiter) + 1.

You can have some fun confirming this with the following code:

&SCOPED-DEFINE NumLoops 6000
&SCOPED-DEFINE SmallListLength 10

DEFINE VARIABLE cExpressionToAdd AS CHARACTER NO-UNDO INITIAL "ab".
DEFINE VARIABLE i AS INTEGER NO-UNDO.
DEFINE VARIABLE j AS INTEGER NO-UNDO.
DEFINE VARIABLE cList AS CHARACTER NO-UNDO.
DEFINE VARIABLE iMethodTime AS INTEGER NO-UNDO.
DEFINE VARIABLE iEmptyLoopTime AS INTEGER NO-UNDO.
DEFINE VARIABLE cResults AS CHARACTER NO-UNDO INITIAL "".
/*&SCOPED-DEFINE DELIMITER " OR "*/
&SCOPED-DEFINE DELIMITER ","

ETIME(TRUE).
DO i = 1 TO {&NumLoops}:
END.
iEmptyLoopTime = ETIME.

cResults = cResults + "Delimiter:" + {&DELIMITER}.
cResults = cResults + SUBSTITUTE("~n One long list of &1 items:", {&NumLoops}).
cList = "".
ETIME(TRUE).
DO i = 1 TO {&NumLoops}:
  cList = cList + MIN({&DELIMITER}, cList) + cExpressionToAdd.
END.
iMethodTime = ETIME.
cResults = cResults + "~n Method 1 cList + MIN(DELIMITER, cList) + cExpressionToAdd:" + STRING(iMethodTime - iEmptyLoopTime).

cList = "".
ETIME(TRUE).
DO i = 1 TO {&NumLoops}:
  cList = cList + (IF cList > "":U
                   THEN {&DELIMITER}
                   ELSE "") + cExpressionToAdd.
END.
iMethodTime = ETIME.
cResults = cResults + "~n Method 2 cList + (IF cList > "":U THEN DELIMITER ELSE "") + cExpressionToAdd:" + STRING(iMethodTime - iEmptyLoopTime).

cList = "".
ETIME(TRUE).
DO i = 1 TO {&NumLoops}:
  cList = cList + cExpressionToAdd + {&DELIMITER}.
END.
cList = RIGHT-TRIM(cList, {&DELIMITER}).
iMethodTime = ETIME.
cResults = cResults + "~n Method 3 cList + cExpressionToAdd + DELIMITER... cList = RIGHT-TRIM(cList, DELIMITER)):" + STRING(iMethodTime - iEmptyLoopTime).

cList = "".
ETIME(TRUE).
DO i = 1 TO {&NumLoops}:
  cList = cList + {&DELIMITER} + cExpressionToAdd.
END.
cList = SUBSTRING(cList, LENGTH({&DELIMITER}) + 1).
iMethodTime = ETIME.
cResults = cResults + "~n Method 4 cList + DELIMITER + cExpressionToAdd... cList = SUBSTRING(cList, LENGTH(DELIMITER)):" + STRING(iMethodTime - iEmptyLoopTime).

cList = "".
ETIME(TRUE).
DO i = 1 TO {&NumLoops}:
  cList = cList + cExpressionToAdd + {&DELIMITER}.
END.
cList = SUBSTRING(cList, 1, LENGTH(cList) - LENGTH({&DELIMITER})).
iMethodTime = ETIME.
cResults = cResults + "~n Method 5 cList + cExpressionToAdd + DELIMITER... cList = SUBSTRING(cList, 1, LENGTH(cList) - LENGTH(DELIMITER)):" + STRING(iMethodTime - iEmptyLoopTime).

cList = "".
ETIME(TRUE).
DO i = 1 TO {&NumLoops}:
  cList = IF i = 1
          THEN cExpressionToAdd
          ELSE cList + {&DELIMITER} + cExpressionToAdd.
END.
iMethodTime = ETIME.
cResults = cResults + "~n Method 6 cList = IF i = 1 THEN cExpressionToAdd ELSE cList + DELIMITER + cExpressionToAdd.:" + STRING(iMethodTime - iEmptyLoopTime).

ETIME(TRUE).
DO j = 1 TO {&NumLoops}:
  DO i = 1 TO {&SmallListLength}:
  END.
END.
iEmptyLoopTime = ETIME.

cResults = cResults + SUBSTITUTE("~n~n Many small lists of length &1:", {&SmallListLength}).

ETIME(TRUE).
DO j = 1 TO {&NumLoops}:
  cList = "".
  DO i = 1 TO {&SmallListLength}:
    cList = cList + MIN({&DELIMITER}, cList) + cExpressionToAdd.
  END.
END.
iMethodTime = ETIME.
cResults = cResults + "~n Method 1 cList + MIN(DELIMITER, cList) + cExpressionToAdd:" + STRING(iMethodTime - iEmptyLoopTime).

ETIME(TRUE).
DO j = 1 TO {&NumLoops}:
  cList = "".
  DO i = 1 TO {&SmallListLength}:
    cList = cList + (IF cList > "":U
                     THEN {&DELIMITER}
                     ELSE "") + cExpressionToAdd.
  END.
END.
iMethodTime = ETIME.
cResults = cResults + "~n Method 2 cList + (IF cList > "":U THEN DELIMITER ELSE "") + cExpressionToAdd:" + STRING(iMethodTime - iEmptyLoopTime).

ETIME(TRUE).
DO j = 1 TO {&NumLoops}:
  cList = "".
  DO i = 1 TO {&SmallListLength}:
    cList = cList + cExpressionToAdd + {&DELIMITER}.
  END.
  cList = RIGHT-TRIM(cExpressionToAdd, {&DELIMITER}:U).
END.
iMethodTime = ETIME.
cResults = cResults + "~n Method 3 cList + cExpressionToAdd + DELIMITER... cList = RIGHT-TRIM(cList, DELIMITER)):" + STRING(iMethodTime - iEmptyLoopTime).

ETIME(TRUE).
DO j = 1 TO {&NumLoops}:
  cList = "".
  DO i = 1 TO {&SmallListLength}:
    cList = cList + {&DELIMITER} + cExpressionToAdd.
  END.
  cList = SUBSTRING(cList, LENGTH({&DELIMITER}) + 1).
END.
iMethodTime = ETIME.
cResults = cResults + "~n Method 4 cList + DELIMITER + cExpressionToAdd... cList = SUBSTRING(cList, LENGTH(DELIMITER)):" + STRING(iMethodTime - iEmptyLoopTime).

ETIME(TRUE).
DO j = 1 TO {&NumLoops}:
  cList = "".
  DO i = 1 TO {&SmallListLength}:
    cList = cList + cExpressionToAdd + {&DELIMITER}.
  END.
  cList = SUBSTRING(cList, 1, LENGTH(cList) - LENGTH({&DELIMITER})).
END.
iMethodTime = ETIME.
cResults = cResults + "~n Method 5 cList + cExpressionToAdd + DELIMITER... cList = SUBSTRING(cList, 1, LENGTH(cList) - LENGTH(DELIMITER)):" + STRING(iMethodTime - iEmptyLoopTime).

ETIME(TRUE).
DO j = 1 TO {&NumLoops}:
  cList = "".
  DO i = 1 TO {&SmallListLength}:
    cList = IF i = 1
            THEN cExpressionToAdd
            ELSE cList + {&DELIMITER} + cExpressionToAdd.
  END.
END.
iMethodTime = ETIME.
cResults = cResults + "~n Method 6 cList = IF i = 1 THEN cExpressionToAdd ELSE cList + DELIMITER + cExpressionToAdd.:" + STRING(iMethodTime - iEmptyLoopTime).

MESSAGE cResults VIEW-AS ALERT-BOX.

Some results on my machine:
Delimiter:,
One long list of 6000 items:
Method 1 cList + MIN(DELIMITER, cList) + cExpressionToAdd:21
Method 2 cList + (IF cList > ":U THEN DELIMITER ELSE ") + cExpressionToAdd:23
Method 3 cList + cExpressionToAdd + DELIMITER... cList = RIGHT-TRIM(cList, DELIMITER)):14
Method 4 cList + DELIMITER + cExpressionToAdd... cList = SUBSTRING(cList, LENGTH(DELIMITER)):14
Method 5 cList + cExpressionToAdd + DELIMITER... cList = SUBSTRING(cList, 1, LENGTH(cList) - LENGTH(DELIMITER)):14
Method 6 cList = IF i = 1 THEN cExpressionToAdd ELSE cList + DELIMITER + cExpressionToAdd.:16

Many small lists of length 10:
Method 1 cList + MIN(DELIMITER, cList) + cExpressionToAdd:40
Method 2 cList + (IF cList > ":U THEN DELIMITER ELSE ") + cExpressionToAdd:47
Method 3 cList + cExpressionToAdd + DELIMITER... cList = RIGHT-TRIM(cList, DELIMITER)):33
Method 4 cList + DELIMITER + cExpressionToAdd... cList = SUBSTRING(cList, LENGTH(DELIMITER)):34
Method 5 cList + cExpressionToAdd + DELIMITER... cList = SUBSTRING(cList, 1, LENGTH(cList) - LENGTH(DELIMITER)):35
Method 6 cList = IF i = 1 THEN cExpressionToAdd ELSE cList + DELIMITER + cExpressionToAdd.:43