#TSV-String: Add Thousands Separators
Formats the specified range of columns from START index to END index of the TSV (END not included). The index starts from “0”. Negative index counts back from the end. If START is omitted, it will start from the beginning, if END is omitted, it will go to the end.
Configs for this Auto Step
- StrConfA
- A: Set Input TSV String *#{EL}
- OptionConfB0
- B0: Select Thousands Separator *
- StrConfB1
- B1: Set Start-Index (Col-A: “0”, Col-C: “2”, Last-Col: “-1”)#{EL}
- StrConfB2
- B2: Set END-Index (Col-A: “0”, Col-C: “2”, Last-Col: “-1”)#{EL}
- SelectConfC
- C: Select STRING DATA to store Output TSV (update) *
- SelectConfInY
- InY: Select NUMERIC for Number of Input TSV Lines (update)
- SelectConfInX
- InX: Select NUMERIC for Number of Input TSV Cols (update)
- SelectConfOutY
- OutY: Select NUMERIC for Number of Output TSV Lines (update)
- SelectConfOutX
- OutX: Select NUMERIC for Number of Output TSV Cols (update)
Script (click to open)
// Script Example of Business Process Automation
// for 'engine type: 3' ("GraalJS standard mode")
// cf. 'engine type: 2' ("GraalJS Nashorn compatible mode") (renamed from "GraalJS" at 20230526)
//////// START "main()" /////////////////////////////////////////////////////////////////
main();
function main(){
//// == Config Retrieving / 工程コンフィグの参照 ==
const strTsv = configs.get ( "StrConfA" ); // REQUIRED
if( strTsv === "" ){
throw new Error( "\n AutomatedTask ConfigError:" +
" Config {A: TSV} is empty \n" );
}
const arr2dTsv = parseAsRectangular ( strTsv ); // as arr2dTSV[Y][X]
const strSeparator = configs.get ( "OptionConfB0" ); /// REQUIRED (SELECT_ITEM)
// "comma", "dot", "space"
const strStartIndex = configs.get ( "StrConfB1" ); // not required
let numStartIndex = Number( strStartIndex );
if ( isNaN(numStartIndex) ) {
numStartIndex = 0;
} else if ( numStartIndex < 0 ) {
numStartIndex = arr2dTsv[0].length + numStartIndex;
}
engine.log( " AutomatedTask Runtime: StartIndex auto-set as " + numStartIndex );
if( arr2dTsv[0].length <= numStartIndex ){
throw new Error( "\n AutomatedTask ConfigError:" +
" Start-Index must be less than the number of columns. \n" );
}
const strEndIndex = configs.get ( "StrConfB2" ); // not required
let numEndIndex = strEndIndex.trim() === "" ? NaN : Number( strEndIndex );
if ( isNaN(numEndIndex) ) {
numEndIndex = arr2dTsv[0].length;
} else if ( numEndIndex < 0 ) {
numEndIndex = arr2dTsv[0].length + numEndIndex;
}
engine.log( " AutomatedTask Runtime: EndIndex auto-set as " + numEndIndex );
if( numEndIndex <= numStartIndex ){
throw new Error( "\n AutomatedTask ConfigError:" +
" Start-Index must be less than End-Index. \n" );
}
const strPocketC = configs.getObject ( "SelectConfC" ); // REQUIRED
const numPocketInY = configs.getObject ( "SelectConfInY" ); // not required
const numPocketInX = configs.getObject ( "SelectConfInX" ); // not required
const numPocketOutY = configs.getObject ( "SelectConfOutY" ); // not required
const numPocketOutX = configs.getObject ( "SelectConfOutX" ); // not required
//// == Data Retrieving / ワークフローデータの参照 ==
// (Nothing. Retrieved via Expression Language in Config Retrieving)
//// == Calculating / 演算 ==
let arrOutputTsv = [];
for ( let i = 0; i < arr2dTsv.length; i++ ) {
let arrRow = arr2dTsv[i]; // 元の行データ
let arrOutputRow = [];
for ( let j = 0; j < arrRow.length; j++ ) { // Determine whether the column is to be processed
if (j >= numStartIndex && j < numEndIndex) { // The column to be processed is separated by thousands.
let processedValue = arrRow[j];
if (strSeparator === "comma") {
processedValue = processedValue.replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1,');
} else if (strSeparator === "dot") {
processedValue = processedValue
.replace(/(\d)\.(?=\d)/g, '$1,') // Temporarily change the decimal point to a comma
.replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1.') // Dot separator
.replace(/(\d),(\d)/g, '$1.$2'); // Convert a temporary comma back to a dot
} else if (strSeparator === "space") {
processedValue = processedValue
.replace(/(\d)\.(?=\d)/g, '$1,') // Temporarily change the decimal point to a comma
.replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1 ') // Digit separator to space
.replace(/(\d),(\d)/g, '$1.$2'); // Convert a temporary comma back to a dot
}
arrOutputRow.push(processedValue);
} else {
arrOutputRow.push(arrRow[j]); // For columns not subject to processing, the original values are used as is.
}
}
arrOutputTsv.push(arrOutputRow.join('\t'));
}
//// == Data Updating / ワークフローデータへの代入 ==
/// ref) Retrieving / Updating from ScriptTasks
/// https://questetra.zendesk.com/hc/en-us/articles/360024574771-R2301
/// https://questetra.zendesk.com/hc/ja/articles/360024574771-R2301
if ( strPocketC !== null ){
engine.setData( strPocketC, arrOutputTsv?.join( '\n' ) ?? "" );
}
if ( numPocketInY !== null ){
engine.setData( numPocketInY, new java.math.BigDecimal( arr2dTsv.length ) );
}
if ( numPocketInX !== null ){
engine.setData( numPocketInX, new java.math.BigDecimal( arr2dTsv[0].length ) );
}
if ( numPocketOutY !== null ){
engine.setData( numPocketOutY, new java.math.BigDecimal( arrOutputTsv.length ) );
}
if ( numPocketOutX !== null ){
engine.setData( numPocketOutX, new java.math.BigDecimal( arrOutputTsv[0].split("\t").length ) );
}
} //////// END "main()" /////////////////////////////////////////////////////////////////
/**
* Parses a TSV (Tab-Separated Values) string and returns a 2D array with rectangular shape.
* This function ensures that each row in the resulting 2D array has the same number of columns,
* equal to the widest row in the original TSV input. Shorter rows are padded with empty strings.
* Blank lines in the input are ignored.
* Additionally, the function logs the minimum and maximum column counts (excluding blank lines),
* along with the total number of lines and count of blank lines.
*
* TSV(タブ区切り値)形式の文字列を解析し、2次元の配列として整形して返します。
* 入力されたTSV文字列を行単位で分割し、各行のセル数をそろえて「長方形(矩形)」の形に整えます。
* 最もセル数が多い行に合わせて、セル数が足りない行には空文字列("")を補完します。
* 空行(中身が空の行)は無視されます。
* また、最小列数・最大列数・全体の行数・空行の数をログに出力します(engine.log)。
*
*
* @param {string} strTsv - The raw TSV string, with rows separated by newline characters (`\n`)
* and cells separated by tab characters (`\t`).
*
* @returns {string[][]} A two-dimensional array where each sub-array represents a row from the TSV input,
* padded with empty strings to ensure uniform column width.
*
* @example
* const tsv = "a\tb\tc\n1\t2\nx\ty\tz\tw\n";
* const result = parseAsRectangular(tsv);
* // result:
* // [
* // ["a", "b", "c", ""],
* // ["1", "2", "", ""],
* // ["x", "y", "z", "w"]
* // ]
*/
function parseAsRectangular( strTsv ){
const arrTsv = strTsv.split("\n");
/// Get numMinWidth and numMaxWidth (blank lines are excluded)
let numMinWidth = Infinity; // cf. String-Type Max: 1 million
let numMaxWidth = 0;
let numBlanklines = 0;
for( let i = 0; i < arrTsv.length; i++ ){
if( arrTsv[i] === "" ){ // Skip blank lines
numBlanklines += 1;
continue;
}
let arrCells = arrTsv[i].split("\t");
if( numMinWidth > arrCells.length ){ numMinWidth = arrCells.length; }
if( numMaxWidth < arrCells.length ){ numMaxWidth = arrCells.length; }
}
engine.log( " AutomatedTask TsvDataCheck:" +
" MinWidth:" + numMinWidth +
" MaxWidth:" + numMaxWidth +
" Lines:" + arrTsv.length +
" (BlankLines:" + numBlanklines + ")" );
/// Get numMinWidth and numMaxWidth (blank lines are excluded)
let arr2dTsv = [];
for( let i = 0; i < arrTsv.length; i++ ){
if( arrTsv[i] === "" ){ // Skip blank lines
continue;
}
let arrTmp = [];
let arrCells = arrTsv[i].split("\t");
for( let j = 0; j < numMaxWidth; j++ ){
if( j < arrCells.length ){
arrTmp[j] = arrCells[j];
}else{
arrTmp[j] = "";
}
}
arr2dTsv.push( arrTmp );
}
return arr2dTsv;
}
/*
### NOTES
- When the process reaches this [Automated Step], the "Formatting" is automatically executed.
- Helps improve the visibility of numerical data.
- Convenient for pre-processing of data sharing (such as email sending, posting to CollabChat, etc.)
- The specified column range (from the START index to just before the END index) is automatically extracted.
- The column index starts at "0" (counting from the beginning, first column is `0`).
- A "negative value" can also be set for the column index (counting from the end, last column is `-1`).
- If the START index is omitted, extraction will start from the first column of the TSV.
- If the END index is omitted, extraction will start up to the last column of the TSV.
- Detects numbers with more than three digits in the Cell text.
- Adds thousand separator to each number.
- A comma, dot, or space can be specified as the digit separator.
- Note that this replacement will also be applied to decimals, postal codes and years.
### NOTES-ja
- 案件プロセスがこの自動工程に到達した際、TSVテキストが読み込まれ「フォーマット処理」が自動実行されます
- 数値データの視認性を向上させるのに役立ちます
- データ共有(メール送信やCollabChatへの自動投稿など)の前処理として便利なアドオン自動工程です
- 指定の列範囲(STARTインデックスからENDインデックスの手前まで)が、自動抽出されます
- 列インデックスは「0」から始まります(先頭から数える/先頭列は `0`)
- 列インデックスには「負の値」も設定可能です(末尾から数える/末尾列は `-1`)
- STARTインデックス指定が省略された場合、TSVの最初の列から抽出されます
- ENDインデックス指定が省略された場合、TSVの最後の列までが抽出されます
- 各セルテキスト中の4桁以上数値(数字4連続以上)を検出します
- 各数値に対してケタ区切記号を付与します
- 桁区切り記号には、カンマ、ドットもしくはスペースが指定可能です
- この自動変換は、小数部や郵便番号や年号にも適用されてしまう点に注意が必要です
### APPENDIX
- TSV (Tab Separated Values) text is assumed to be rectangular data.
- TSV text: monthly sales summary, monthly access summary, PIVOT table, etc.
- Non-rectangular data is automatically formatted with an empty string (adjusted to the longest number of columns).
- Blank lines (including trailing newlines) are ignored.
- Indexes:
- Indexes start from 0 (first column is `0`).
- For example, the third column from the left (so-called column C) has index `2`.
- Negative indexes are counted from the end (last column is `-1`).
- For example, the third column from the right has index `-3`.
- The number of rows (height) and columns (width) of the input/output TSV can be stored in Numeric data.
- Examples:
- Input: `$12345678.90`
- Output: `$12,345,678.90`
- Input: `€12345.00`
- Output: `€12,345.00`
- Input: `12345678.90123`
- Output: `12,345,678.90,123` (not good)
- Input: `0120-345-678`
- Output: `0,120-345-678` (not good)
### APPENDIX-ja
- TSV(Tab Separated Values)テキストは、矩形データを前提とします
- TSVテキスト: 月次売上集計、月次アクセス集計、PIVOTテーブル、など
- 矩形でないデータは、空文字列によって自動整形されます(最も長い列数に合わせる)
- 空行(末尾改行を含む)は無視されます
- インデックスの理解:
- インデックスは0から始まります(先頭列は `0`)
- 例えば、左から3番目の列(いわゆるC列)は、インデックス `2` となります
- 負のインデックスは末尾から数えます(末尾列は `-1`)
- 例えば、右から3番目の列は、インデックス `-3` となります
- インプットTSVおよびアウトプットTSVの行数(高さ)と列数(幅)を数値型データ項目に格納できます
- Examples:
- Input: `$12345678.90`
- Output: `$12,345,678.90`
- Input: `€12345.00`
- Output: `€12,345.00`
- Input: `12345678.90123`
- Output: `12,345,678.90,123` (好ましくない例)
- Input: `0120-345-678`
- Output: `0,120-345-678` (好ましくない例)
*/
Download
- tsv-string-add-thousands-separators-2025.xml
- 2025-07-14 (C) Questetra, Inc. (MIT License)
Freely modifiable JavaScript (ECMAScript) code. No warranty of any kind.
(Installing Addon Auto-Steps are available only on the Professional edition.)
(Installing Addon Auto-Steps are available only on the Professional edition.)
Notes
- When the process reaches this [Automated Step], the formatting is automatically executed.
- Helps improve the visibility of numerical data.
- Convenient for pre-processing of data sharing (such as email sending, posting to CollabChat, etc.)
- The specified column range (from the START index to just before the END index) is automatically extracted.
- The column index starts at “0” (counting from the beginning, first column is
0). - A negative value can also be set for the column index (counting from the end, last column is
-1). - If the START index is omitted, extraction will start from the first column of the TSV.
- If the END index is omitted, extraction will start up to the last column of the TSV.
- The column index starts at “0” (counting from the beginning, first column is
- Detects numbers with more than three digits in the Cell text.
- Adds thousand separator to each number.
- A comma, dot, or space can be specified as the digit separator.
- Note that this replacement will also be applied to decimals, postal codes and years.
Capture


Appendix
- TSV (Tab Separated Values) text is assumed to be rectangular data.
- TSV text: monthly sales summary, monthly access summary, PIVOT table, etc.
- Non-rectangular data is automatically formatted with an empty string (adjusted to the longest number of columns).
- Blank lines (including trailing newlines) are ignored.
- Indexes:
- Indexes start from 0 (first column is
0). - For example, the third column from the left (so-called column C) has index
2. - Negative indexes are counted from the end (last column is
-1). - For example, the third column from the right has index
-3.
- Indexes start from 0 (first column is
- The number of rows (height) and columns (width) of the input/output TSV can be stored in Numeric data.
- Examples:
- Input:
$12345678.90 - Output:
$12,345,678.90 - Input:
?12345.00 - Output:
?12,345.00 - Input:
12345678.90123 - Output:
12,345,678.90,123(bad example) - Input:
0120-345-678 - Output:
0,120-345-678(bad example)
- Input:
