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