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