#TSV文字列: 数値列の四則演算

#TSV文字列: 数値列の四則演算

#TSV文字列: 数値列の四則演算

translate #TSV-String: Basic Math on Numeric Column

数値演算の結果を新しい列として追加します。「A列とB列の加算」や「C列と “1.1” の乗算」といった2値の演算結果がTSV各行の行末に追加されます。加算、減算、乗算、除算、剰余、べき乗に対応しています。

Auto Step icon
Configs for this Auto Step
StrConfA
A: 入力TSV文字列をセットしてください *#{EL}
BoolConfAx
Ax: 1行目をヘッダ行とみなさない ⇔ 1行目をヘッダ行とみなす
OptionConfB
B: 演算子を選択してください *
StrConfX
X: 第1オペランド(変数X)となる列IDをセットしてください(A列:”0″, C列:”2″)#{EL}
StrConfXv
Xv: 第1オペランド(X)のデフォルト値をセットしてください(例: “1.1”)#{EL}
StrConfY
Y: 第2オペランド(変数Y)となる列IDをセットしてください(B列:”1″, D列:”3″)#{EL}
StrConfYv
Yv: 第2オペランド(Y)のデフォルト値をセットしてください(例: “1.1”)#{EL}
SelectConfC
C: アウトプットTSVが格納される複数行文字列型データを選択してください (更新) *
BoolConfCx
Cx: 桁区切り文字: Off ⇔ On
SelectConfDy
Dy: インプットTSVの行数(高さ)を格納する数値型データを選択してください (更新)
SelectConfDx
Dx: インプットTSVの列数(幅)を格納する数値型データを選択してください (更新)
SelectConfEy
Ey: アウトプットTSVの行数(高さ)が格納される数値型データを選択してください (更新)
SelectConfEx
Ex: アウトプットTSVの列数(幅)が格納される数値型データを選択してください (更新)
StrConfF
F: 桁区切りOnの時、小数部に使用する最小桁数をセットしてください(default: “0”)#{EL}
StrConfG
G: 桁区切りOnの時、小数部に使用する最大桁数をセットしてください(default: “3”)#{EL}
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" );
  }
  // get TSV as Array-Array (2d Array)
  const arr2dTsv    = parseAsRectangular ( strTsv );  // [rowY,colX]
  let numInputTsvLines = arr2dTsv.length;
  let numInputTsvCols  = arr2dTsv[0].length;

const boolHeaderOn  = configs.getObject  ( "BoolConfAx" );     // TOGGLE
  // https://questetra.zendesk.com/hc/ja/articles/360024574471-R2300 "Boolean object"

const strOperator   = configs.get        ( "OptionConfB" );    /// REQUIRED (SELECT_ITEM)
  // "add", "subtract", "multiply", "divide", "remainder", "power"

const strOperandXid = configs.get        ( "StrConfX" );       // not required
  let numOperandXid = parseInt( strOperandXid, 10 );   // Int or NaN
const strOperandXv  = configs.get        ( "StrConfXv" );      // not required
  let numOperandXv  = parseFloat( strOperandXv );      // Float or NaN
const strOperandYid = configs.get        ( "StrConfY" );       // not required
  let numOperandYid = parseInt( strOperandYid, 10 );   // Int or NaN
const strOperandYv  = configs.get        ( "StrConfYv" );      // not required
  let numOperandYv  = parseFloat( strOperandYv );      // Float or NaN

const strPocketC     = configs.getObject ( "SelectConfC" );    // REQUIRED
const boolSeparatorOn = configs.getObject ( "BoolConfCx" );    // TOGGLE

const numPocketDy    = configs.getObject ( "SelectConfDy" );   // not required
const numPocketDx    = configs.getObject ( "SelectConfDx" );   // not required
const numPocketEy    = configs.getObject ( "SelectConfEy" );   // not required
const numPocketEx    = configs.getObject ( "SelectConfEx" );   // not required

const strDigitMin    = configs.get       ( "StrConfF" );       // not required
  let numDigitMin    = parseInt( strDigitMin, 10 );   // Int or NaN
const strDigitMax    = configs.get       ( "StrConfG" );       // not required
  let numDigitMax    = parseInt( strDigitMax, 10 );   // Int or NaN



//// == Data Retrieving / ワークフローデータの参照 ==
// (Nothing. Retrieved via Expression Language in Config Retrieving)



//// == Calculating / 演算 ==
let arrOutputTsv  = [];

if ( boolHeaderOn ){
  let arrTmp = arr2dTsv.shift();   // Remove First Element
  arrTmp.push ( strOperator );
  arrOutputTsv.push ( arrTmp.join( '\t' ) );
}

for ( let i = 0; i < arr2dTsv.length; i++ ) {
  let arrTmp = arr2dTsv[i].slice();

  let numX;
  if ( !isNaN(numOperandXid) ) {
      numX = parseFloat(arr2dTsv[i][numOperandXid].replace(/,/g, ''));
  }
  if ( isNaN(numX) ) { // numOperandXidがNaNの場合、またはTSVからのパース結果がNaNの場合
      numX = numOperandXv;
  }
  if ( isNaN(numX) ){
    arrTmp.push ( "#N/A" );
    arrOutputTsv.push ( arrTmp.join( '\t' ) );
    continue;
  }

  let numY;
  if ( !isNaN(numOperandYid) ) {
      numY = parseFloat(arr2dTsv[i][numOperandYid].replace(/,/g, ''));
  }
  if ( isNaN(numY) ) { // numOperandYidがNaNの場合、またはTSVからのパース結果がNaNの場合
      numY = numOperandYv;
  }
  if ( isNaN(numY) ){
    arrTmp.push ( "#N/A" );
    arrOutputTsv.push ( arrTmp.join( '\t' ) );
    continue;
  }

  if ( strOperator === 'divide' && numY === 0 ) {
    arrTmp.push("#DIV/0!");
    arrOutputTsv.push(arrTmp.join('\t'));
    continue;
  }

  let numResult = 0;
  switch ( strOperator ) {
    case 'add':       numResult = numX + numY; break;
    case 'subtract':  numResult = numX - numY; break;
    case 'multiply':  numResult = numX * numY; break;
    case 'divide':    numResult = numX / numY; break;
    case 'remainder': numResult = numX % numY; break;
    case 'power':     numResult = numX ** numY; break;
    default:          throw new Error( "\n AutomatedTask RuntimeError:" +
                        " Unsupported operator: " + strOperator + "\n" );
  }

  if ( boolSeparatorOn ){
    let formatOptions = {};
    if ( !isNaN(numDigitMin) ) formatOptions.minimumFractionDigits = numDigitMin;
    if ( !isNaN(numDigitMax) ) formatOptions.maximumFractionDigits = numDigitMax;
    arrTmp.push( numResult.toLocaleString( undefined, formatOptions ) );
  } else {
    arrTmp.push ( numResult );
  }

  arrOutputTsv.push ( arrTmp.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 ( numPocketDy !== null ){ 
  engine.setData( numPocketDy, new java.math.BigDecimal( numInputTsvLines ) );
}
if ( numPocketDx !== null ){ 
  engine.setData( numPocketDx, new java.math.BigDecimal( numInputTsvCols ) );
}
if ( numPocketEy !== null ){ 
  engine.setData( numPocketEy, new java.math.BigDecimal( arrOutputTsv.length ) );
}
if ( numPocketEx !== null ){ 
  engine.setData( numPocketEx, 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 "calculation process" is automatically executed.
    - The calculation result is added to the rightmost column at the output TSV.
    - Binary operations can be set using numeric column X and numeric column Y (column IDs starting from zero).
    - One of XY can also be set to a fixed value.
- TSV (Tab Separated Values) text is assumed to be rectangular data.
    - TSV text: monthly sales tally, monthly access tally, etc.
    - Data that is not rectangular is automatically formatted with blank characters.
    - Blank lines (including trailing newlines) are ignored.
- Numeric columns are parsed with JavaScript `parseFloat()` after removing commas (thousand separators).
    - The characters accepted are plus/minus sign, decimal digits, decimal point (.), exponent indicator, and the `Infinity` literal.
    - If it cannot be converted to a number, it is calculated using the "default value".
    - If no column ID is specified for XY, it is calculated using the "default value" (fixed value).
    - If the "default value" is also not set, `#N/A` will be output.
    - If the value of Y is "0" during division, `#DIV/0!` will be output.
- To arrange the sort order of the output TSV, place "TSV String, Convert" downstream in the workflow.
    - https://support.questetra.com/addons/tsv-string-convert-2021/

### NOTES-ja
- 案件プロセスがこの自動工程に到達した際、TSVテキストが読み込まれ「計算処理」が自動実行されます
    - 計算結果が最右列に列追加されたTSVが出力されます
    - 数値列Xと数値列Yによる二項演算が設定可能です(列IDはゼロ始まりで設定してください)
    - XYの一方を固定値に設定することも可能です
- TSV(Tab Separated Values)テキストは、矩形データを前提とします
    - TSVテキスト: 月次売上集計、月次アクセス集計、など
    - 矩形でないデータは、空文字によって自動整形されます
    - 空行(末尾改行を含む)は無視されます
- 数値列は、カンマ(桁区切り文字)を除去したうえで、JavaScript `parseFloat()` で解析されます
    - 正負符号、数値、小数点 (.)、指数記号、および `Infinity` が認識可能です
    - (以外の文字に遭遇したら、その文字の前までの値を返します)
    - 数値に変換できなかった場合は、「デフォルト値」にて計算されます
    - XYに列IDを指定しなかった場合は、「デフォルト値」にて計算されます(固定値)
    - 「デフォルト値」も未設定だった場合、 `#N/A` が出力されます
    - 除算時にYの値が "0" だった場合、 `#DIV/0!` が出力されます
- 出力TSVの並び順を変更したい場合は、ワークフロー下流に『TSV 文字列, 変換』を配置します。
    - https://support.questetra.com/ja/addons/tsv-string-convert-2021/

### APPENDIX
- If the first line is a header row, the column name of the calculation result will be the operator name.
    - "add", "subtract", "multiply", "divide", "remainder" or "power"
- e.g.: Add column A (column ID: "0") and column B (column ID: "1")
    - Operator: `Addition: X+Y`
    - Column ID of X: `0`
    - Column ID of Y: `1`
- e.g.: Calculate 1.1 times the value of column C (column ID: "2")
    - Operator: `Multiplication: X*Y`
    - Column ID of X: `2`
    - Default value of X: `0`
    - Column ID of Y: `null` (not set)
    - Default value of Y: "1.1"

### APPENDIX-ja
- 1行目がヘッダ行の場合、計算結果列の列名は演算子名となります
    - "add", "subtract", "multiply", "divide", "remainder", "power"
- eg: A列(列ID:"0")とB列(列ID:"1") の加算
    - 演算子: `加算: X+Y`
    - Xの列ID: `0`
    - Yの列ID: `1`
- eg: C列 (列ID:"2") の値の1.1倍を計算
    - 演算子: `乗算: X*Y`
    - Xの列ID: `2`
    - Xのデフォルト値: `0`
    - Yの列ID: `null` (未設定)
    - Yのデフォルト値: `1.1`
*/

Download

warning 自由改変可能な JavaScript (ECMAScript) コードです。いかなる保証もありません。
(アドオン自動工程のインストールは Professional editionでのみ可能です)

Notes

  • 案件プロセスがこの自動工程に到達した際、TSVテキストが読み込まれ「計算処理」が自動実行されます
    • 計算結果が最右列に列追加されたTSVが出力されます
    • 数値列Xと数値列Yによる二項演算が設定可能です(列IDはゼロ始まりで設定してください)
    • XYの一方を固定値に設定することも可能です
  • TSV(Tab Separated Values)テキストは、矩形データを前提とします
    • TSVテキスト: 月次売上集計、月次アクセス集計、など
    • 矩形でないデータは、空文字によって自動整形されます
    • 空行(末尾改行を含む)は無視されます
  • 数値列は、カンマ(桁区切り文字)を除去したうえで、JavaScript parseFloat() で解析されます
    • 正負符号、数値、小数点 (.)、指数記号、および Infinity が認識可能です
    • (以外の文字に遭遇したら、その文字の前までの値を返します)
    • 数値に変換できなかった場合は、「デフォルト値」にて計算されます
    • XYに列IDを指定しなかった場合は、「デフォルト値」にて計算されます(固定値)
    • 「デフォルト値」も未設定だった場合、 #N/A が出力されます
    • 除算時にYの値が “0” だった場合、 #DIV/0! が出力されます
  • 出力TSVの並び順を変更したい場合は、ワークフロー下流に『TSV 文字列, 変換』を配置します。

Capture

#TSV文字列: 数値列の四則演算

Appendix

  • 1行目がヘッダ行の場合、計算結果列の列名は演算子名となります
    • “add”, “subtract”, “multiply”, “divide”, “remainder”, “power”
  • eg: A列(列ID:”0″)とB列(列ID:”1″) の加算
    • 演算子: 加算: X+Y
    • Xの列ID: 0
    • Yの列ID: 1
  • eg: C列 (列ID:”2″) の値の1.1倍を計算
    • 演算子: 乗算: X*Y
    • Xの列ID: 2
    • Xのデフォルト値: 0
    • Yの列ID: null (未設定)
    • Yのデフォルト値: 1.1

See Also

上部へスクロール

Questetra Supportをもっと見る

今すぐ購読し、続きを読んで、すべてのアーカイブにアクセスしましょう。

続きを読む