Google Apps Script Utilities.parseCsv() change decimal and thousand separator

Viewed 855

I am new to GAS and I am struggling badly with the problem that I have. (I haven't found a similar question on the site that would have solved my problem, therefore I am asking a new one)

Goal: Import CSV from Google Drive into Google Sheets
Problem:
Currencies in the csv file are "1,000.57" --> US format
Currency format that I need "1.000,57" --> European format

Currently with the Utilities.parseCsv() the formats just gets messed up and the currencies are plain wrong.

Question: Is there a way to change "," to "." and "." to "," during the parse? If so, will there be further problems since the delimiter for the csv is "," as well.

I already found some code snippets in the web (not my code: props to spreadsheet.dev) and tried to change the following, but it does not seem to work:

//Imports a CSV file in Google Drive into the Google Sheet
function importCSVFromDrive() {
  var fileName = promptUserForInput("Please enter the name of the CSV file to import from Google Drive:");
  var files = findFilesInDrive(fileName);
  if(files.length === 0) {
    displayToastAlert("No files with name \"" + fileName + "\" were found in Google Drive.");
    return;
  } else if(files.length > 1) {
    displayToastAlert("Multiple files with name " + fileName +" were found. This program does not support picking the right file yet.");
    return;
  }
  var file = files[0];
  var csvString = file.getBlob().getDataAsString()
  var escapedString = csvString.replace(",",".")
                                .replace(".",",");
  var contents = Utilities.parseCsv(escapedString);
  var sheetName = writeDataToSheet(contents);
  displayToastAlert("The CSV file was successfully imported into " + sheetName + ".");
}

//Prompts the user for input and returns their response
function promptUserForInput(promptText) {
  var ui = SpreadsheetApp.getUi();
  var prompt = ui.prompt(promptText);
  var response = prompt.getResponseText();
  return response;
}

//Returns files in Google Drive that have a certain name.
function findFilesInDrive(filename) {
  var files = DriveApp.getFilesByName(filename);
  var result = [];
  while(files.hasNext())
    result.push(files.next());
  return result;
}

//Inserts a new sheet and writes a 2D array of data in it
function writeDataToSheet(data) {
  var ss = SpreadsheetApp.getActive();
  sheet = ss.insertSheet();
  sheet.getRange(1, 1, data.length, data[0].length).setValues(data);
  return sheet.getName();
}

What am I doing wrong?

3 Answers

Utilities.parseCsv() is a hot mess. I recommend you not to use it. Instead, try the Advanced Google Service - Drive V2

You will need to add Drive under Services.

Here is the code snippet you will need:

function insertFromCsv(fileName) {
  var blob = DriveApp.getFilesByName(fileName).next().getBlob();
  var tempFile = Drive.Files.insert({title: "tempSheet"}, blob, {
      convert: true
  });
  var tempSsId = tempFile.getId();
  var tempSheet = SpreadsheetApp.openById(tempSsId).getSheets()[0];
  var newSheet = tempSheet.copyTo(SpreadsheetApp.getActive());
  DriveApp.getFileById(tempSsId).setTrashed(true);
  return newSheet.getName();
}

and change importCSVFromDrive as follows:

function importCSVFromDrive() {
  var fileName = promptUserForInput("Please enter the name of the CSV file to import from Google Drive:");
  var files = findFilesInDrive(fileName);
  if(files.length === 0) {
    displayToastAlert("No files with name \"" + fileName + "\" were found in Google Drive.");
    return;
  } else if(files.length > 1) {
    displayToastAlert("Multiple files with name " + fileName +" were found. This program does not support picking the right file yet.");
    return;
  }
  var file = files[0];
  // var csvString = file.getBlob().getDataAsString()
  // var escapedString = csvString.replace(",",".")
  //                               .replace(".",",");
  // var contents = Utilities.parseCsv(escapedString);
  // var sheetName = writeDataToSheet(contents);
  var fileName = file.getName();
  var sheetName = insertFromCsv(fileName);

  displayToastAlert("The CSV file was successfully imported into " + sheetName + ".");
}

So sample of your CSV data is here: https://drive.google.com/file/d/1ASevYOWtu8YL6YA4w-UqDuNXAS0RfaJF/view?usp=sharing

It looks for me like just plain CSV data:

Trades,Header,DataDiscriminator,Asset Category,Currency,Symbol,Date/Time,Quantity,T.Price
Trades,Data,Order,Stocks,USD,ALGN,"2021-06-28,10:50:27",3,627.17,621.52,-1881.51,-1,1882.51,0,-16.95,O
Trades,Data,Order,Stocks,USD,AMAT,"2021-06-29,09:38:53",14,142.15,141.92,-1990.1,-1,1991.1,0,-3.22,O
Trades,Data,Order,Stocks,USD,APH,"2021-07-02,09:30:01",30,69.438,69.95,-2083.14,-1,2084.14,0,15.36,O

I see no "european" formatted numbers out there.

I believe it can be parsed correctly into this:

Trades Header DataDiscriminator Asset Category Currency Symbol Date/Time Quantity T.Price
Trades Data Order Stocks USD ALGN "2021-06-28,10:50:27" 3 627.17 621.52 -1881.51 -1 1882.51 0 -16.95 O
Trades Data Order Stocks USD AMAT "2021-06-29,09:38:53" 14 142.15 141.92 -1990.1 -1 1991.1 0 -3.22 O
Trades Data Order Stocks USD APH "2021-07-02,09:30:01" 30 69.438 69.95 -2083.14 -1 2084.14 0 15.36 O

I haven't tried to do it with Utilities.parseCsv(), I wrote my own little csv-parser, just to be sure that the task is doable and my assumptions are correct:

var s = `Trades,Header,DataDiscriminator,Asset Category,Currency,Symbol,Date/Time,Quantity,T.Price
Trades,Data,Order,Stocks,USD,ALGN,"2021-06-28,10:50:27",3,627.17,621.52,-1881.51,-1,1882.51,0,-16.95,O
Trades,Data,Order,Stocks,USD,AMAT,"2021-06-29,09:38:53",14,142.15,141.92,-1990.1,-1,1991.1,0,-3.22,O
Trades,Data,Order,Stocks,USD,APH,"2021-07-02,09:30:01",30,69.438,69.95,-2083.14,-1,2084.14,0,15.36,O`;

// replace ',' with '_' inside quotes
s.match(/("[^,]+),(.+")/g).forEach(t=>s=s.split(t).join(t.replace(/,/g,'_')));

// replace ',' with '\t', replace '_' with ',' and split string into 2-d array
var array = s.replace(/,/g,"\t").replace(/_/g,',').split('\n').map(x => x.split('\t'));

console.table(array);

Output:

┌─────────┬──────────┬──────────┬─────────────────────┬──────────────────┬────────────┬──────────┬─────────────────────────┬────────────┬───────────┬──────────┬────────────┬──────┬───────────┬─────┬──────────┬─────┐
│ (index) │    0     │    1     │          2          │        3         │     4      │    5     │            6            │     7      │     8     │    9     │     10     │  11  │    12     │ 13  │    14    │ 15  │
├─────────┼──────────┼──────────┼─────────────────────┼──────────────────┼────────────┼──────────┼─────────────────────────┼────────────┼───────────┼──────────┼────────────┼──────┼───────────┼─────┼──────────┼─────┤
│    0    │ 'Trades' │ 'Header' │ 'DataDiscriminator' │ 'Asset Category' │ 'Currency' │ 'Symbol' │       'Date/Time'       │ 'Quantity' │ 'T.Price' │          │            │      │           │     │          │     │
│    1    │ 'Trades' │  'Data'  │       'Order'       │     'Stocks'     │   'USD'    │  'ALGN'  │ '"2021-06-28,10:50:27"' │    '3'     │ '627.17'  │ '621.52' │ '-1881.51' │ '-1' │ '1882.51' │ '0' │ '-16.95' │ 'O' │
│    2    │ 'Trades' │  'Data'  │       'Order'       │     'Stocks'     │   'USD'    │  'AMAT'  │ '"2021-06-29,09:38:53"' │    '14'    │ '142.15'  │ '141.92' │ '-1990.1'  │ '-1' │ '1991.1'  │ '0' │ '-3.22'  │ 'O' │
│    3    │ 'Trades' │  'Data'  │       'Order'       │     'Stocks'     │   'USD'    │  'APH'   │ '"2021-07-02,09:30:01"' │    '30'    │ '69.438'  │ '69.95'  │ '-2083.14' │ '-1' │ '2084.14' │ '0' │ '15.36'  │ 'O' │
└─────────┴──────────┴──────────┴─────────────────────┴──────────────────┴────────────┴──────────┴─────────────────────────┴────────────┴───────────┴──────────┴────────────┴──────┴───────────┴─────┴──────────┴─────┘

If you add range.setValues(array) instead of console.table(array) you probably will get a propper table in your sheet.

Update

To replace 123.45 --> 123,45 in the array you need to add one line at the end:

array = array.map(row => row.map(cell => cell.replace(/(\d)\.(\d)/g, '$1,$2')));
Related