How to avoid parsing of data with google app script range.setValues - google-apps-script

I'm dealing with an annoying problem when importing data into Google Spreasheets using Google App Script
Basically the code is as follows:
var csvData = Utilities.parseCsv(mySource,separator);
range.setValues(csvData);
usually this works perfectly but sometimes we have a productcode column which gets messed up
productcode
value
01DECUB003
100
21MAR003
200
The second row gets messed up because the product code is interpreted as a Date and thus converted into 21mar2003 which in turn does not match any of the real product code and then raises errors in further export scritps.
Is there any way to fix this? I don't see any relevant option neither within Utilities nor within Range.
Is there any alternative APIs for doing the same?
Here's an example which reproduces the issue
create a new sheet and name the first (and only) tab "IMPORT"
set the "sheet settings" locale to "Italian"
create and run the following script
function main() {
var csvContent= 'code;value\n01mmabc001;111,1\n21mar003;222,2';
var separator= ';';
var csvData = Utilities.parseCsv(csvContent,separator);
var sheet = SpreadsheetApp.getActiveSheet();
var range;
sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("IMPORT");
range = sheet.getRange(1, 1, csvData.length, csvData[0].length);
range.setValues(csvData);
}

I have been able to do it by changing this:
range = sheet.getRange(1, 1, csvData.length, csvData[0].length);
to this:
range = sheet.getRange(1, 1, csvData.length, csvData[0].length).setNumberFormat('#');
What this does is while getting the range it sets the format of the cells to #:
Inserts the raw text for the cell, if the cell has text input. Not compatible with any of the other special characters and won’t display for numeric values (which are displayed as general format).
Reference:
setNumberFormat(numberFormat)
Number format tokens

Related

How to print the result of a script on the current sheet

I am working with a script that translates a text in a cell of a sheet through the Deepl API.
Thanks to a colleague help it worked correctly (Use Deepl API and google sheets).
In this case, I need to know how I can make the result of the translation to be included in a cell of the same sheet.
My data object is the following. The first script picks up the text from column 2, row 3. It would be perfect if the result is printed in column 2, row 4, but I don't know exactly how to do it.
I include an image of the corresponding data
I am using this script to translate the text of a cell in my sheet:
function deeplapi() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var text = sheet.getRange(3,2).getValue(); // define text before response
var response = UrlFetchApp.fetch("https://api.deepl.com/v2/translate?auth_key=xxxx-xxxx-xxxx-xxxx-xxxx&text="+ text +"&target_lang=en&source_lang=es");
var json = response.getContentText();
var data = JSON.parse(json);
Logger.log(data);
}
This way I get the result in the records section, but with this format:
Información {translations=[{text=Hi, I'm Carlos, detected_source_language=ES}]}
First, I have tried to extract only the translation in this section and then add it in the same sheet. With this I get the exact translation, but I don't know how to include it in a specific range of my sheet.
Logger.log(data.translations[0].text);
In addition to this, I think that to get this result and paste it in the sheet I would have to use:
sheet.getRange () .setValues (sortedOutput);
Anyway, I'm not sure
Try something like that at the bottom of your script to paste the translation text into cell B4:
sheet.getRange('B4').setValue(data.translations[0].text)
Minimal reproducible example:
const data = {translations:[{text:"Hi, I'm Carlos", detected_source_language:"ES"}]};
console.log(data.translations[0].text)

Exception: The starting row of the range is too small - Logs Numbers Why?

I have a basic script to take numbers from a sheet and use them to create a range, as well as using the last column function. I have had the error range is too small for the posting range.
When I log the output for both the column and row numbers these come out as expected!
I thought initially, it was because one was a last column pull and the other was pulling an integer from the cell in the sheets, as they were coming with decimal places, so I have overcome this with the conversion to number and then removing the decimals with the .tofixed() but this does not work either. Any ideas?
function weeklyData() {
var sourcess = SpreadsheetApp.openById('1B93Oq2s9Nou5hVgOb3y3t15t9xnqRMBnrYkAed-oxrE'); // key of source spreadsheet
var sourceSheet = sourcess.getSheetByName('Measures & Answers'); // source sheet name - change to your actual sheet name
var lr = Number(sourceSheet.getRange(2,3).getDataRegion(SpreadsheetApp.Dimension.ROWS).getLastRow()).toFixed(0);
for(var i=0;i<lr+1;i++){
var dataValue = sourceSheet.getRange(i+2,3).getValue(); //This weeks numbers to update into table
var rowdataRange = sourceSheet.getRange(i+2,4).getValue(); //The row that the data needs to be pasted
var rowformat = Number(rowdataRange);
var row = rowformat.toFixed(0);
var pasteSheet = sourcess.getSheetByName('WHERE DATA ENDS UP'); // Data is to be pasted - change to your actual sheet name
var pasteColumn = pasteSheet.getRange(12,12).getDataRegion(SpreadsheetApp.Dimension.COLUMNS).getLastColumn()+1;
var column = pasteColumn.toFixed(0); // Column that is free for this weeks data
var pasteRange = pasteSheet.getRange(row,column,1,1);
Logger.log(pasteRange);
// pasteRange.setValue(dataValue);
}};
Your script works fine for me. I suspect this is an example script you've adapted from somewhere and trying to apply it to your data structure?
The reason you are getting the error is probably because the data in column 4 of your source sheet is not of number format? Either change your data or change the following line to the column containing numeric values.
var rowdataRange = sourceSheet.getRange(i+2,4).getValue();
This script is poorly written for this particular use.
You might want to check your Spreadsheet since it has lots of random values at random ranges.
When you use the following command Logger.log(pasteSheet.getLastColumn()); the number returned is 3753: which means that that is the next available column at which your data will be pasted.
The error message you were getting is due to the fact that the range was incorrect and you were passing wrong values in order to access it, which was most likely because of the values mentioned above.
Moreover, after cleaning all the unnecessary data, you can make use of the below script.
Snippet
function weeklyData() {
var spreadsheet = SpreadsheetApp.openById("ID_OF_THE_SPREADSHEET");
var sourceSheet = spreadsheet.getSheetByName("Measures & Answers");
var pasteSheet = spreadsheet.getSheetByName("WHERE DATA ENDS UP");
var valsToCopy = sourceSheet.getRange(2, 3, sourceSheet.getLastRow(), 1).getValues();
var rowsAt = sourceSheet.getRange(2, 4, sourceSheet.getLastRow(), 1).getValues();
var column = pasteSheet.getRange(12,12).getDataRegion(SpreadsheetApp.Dimension.COLUMNS).getLastColumn()+1;
for (var i = 0; i < valsToCopy.length; i++)
if (rowsAt[i][0] != "")
pasteSheet.getRange(parseInt(rowsAt[i][0]), parseInt(column)).setValue(valsToCopy[i][0].toString());
}
Explanation
The above script gathers all the data that needs to be copied as well as the rows associated with it. In order to make sure you don't end up using inappropriate values for the ranges, an if condition has been placed to make sure the value is not empty.
Reference
Sheet Class Apps Script - getLastColumn();
Sheet Class Apps Script - getRange(row, column, numRows, numColumns);

Requesting a Spreadsheet range value return "You must select all cells in a merged range..."

I'm trying to read in a single cell range in a Google Spreadsheet I've been maintaining for about a year now. Today, I've been getting the following error in my logs:
You must select all cells in a merged range to merge or unmerge them.
I don't have any merged cells in that sheet. I've noticed that the Range method also has a "isPartOfMerge()" method. Trying this on the returned Range object raises the same error as before.
var sht_settings = ss.getSheetByName('Settings');
// lots of code in between
var reg_rate = sht_settings.getRange("B9").getValue();
Normally, I would get an integer correlating to the value in the cell, like "20" or "35". Now just getting the reported error.
Update: Discovered the line that is causing the problem. Running the following sequence allows the correct value to be read.
var reg_rate = sht_settings.getRange(9, 2).getValue();
new_sht.getRange("F3:G3").merge().setValue('INVOICE').setFontSize(18).setFontWeight('bold').setHorizontalAlignment('right');
By changing the getRange() method to refer to a single cell and remove the merge() method allows it all to work.
FYI for anyone dealing with the same issue:
The issue was discovered to be editing another sheet that the code was updating. The other sheet had merged cells and the format was slightly changed. Now the code retrieved ranges with merged cells overlapped. To fix this was by removing all merges in the other sheet and re-running the code.
Instead of reporting these changes on those lines involving the merges/updates, the script was reporting the change somewhere else.
Check the following Spreadsheet I've created to test the issue: https://docs.google.com/spreadsheets/d/14Ym0GQl3fMhlHljGfWZeUbZRTJMQ53vrA3BYBjbNISU/edit?usp=sharing
The code in the script editor looks like
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sht1 = ss.getSheetByName('Sheet1');
var sht2 = ss.getSheetByName('Sheet2');
function myFunction() {
var val1 = sht1.getRange(1, 2).getValue();
sht2.getRange(2, 2, 1, 2).merge().setValue(val1);
var val2 = sht1.getRange(2, 2).getValue();
sht2.getRange(3, 2, 1, 2).merge().setValue(val2);
}
"Sheet1" contains values in B1 and B2, "Sheet2" is blank except for a Merged Cell with B2:C2.
In this example, running myFunction() will error on line 8 instead of line 7.

Find and replace script (Google-apps) for Content grouping

Some of you may be familiar with content grouping in Google Analytics, which basically lets you group any number of URL's in user-specified groups (this is useful for analyzing pages that belong together all at the same time). I'm working on a script to take that to the next level and use it in Google Sheets as well.
Goal: have a working script that rewrites URL's and gives them another name, regardless of whether it uses upper or lower cases in the URL.
So far I have this:
function onOpen() {
var sheet = SpreadsheetApp.getActiveSheet();
var range = sheet.getRange("a1:a10000");
var to_replace = /.*example.*/;
var replace_with = "TEST";
var to_replace2 = /.*another-example.*/;
var replace_with2 = "TEST-Nr2";
replaceInSheet(sheet,range, to_replace, replace_with);
replaceInSheet(sheet,range, to_replace2, replace_with2);
}
This script works in the sense that it rewrites URL's with 'Example' in it to 'Test' and it rewrites 'Another-example' into TEST-Nr2.
However, the final script will probably have thousands of URL's that will need to be rewritten. Furthermore, some URL's have uppercases in them, which I want to ignore and just rewrite.
All of the above leads me to two questions:
How can I write the script in such a way (with regular expressions for example?) that I won't have a Googleplex number of To_replace's and replace_with's?
How can I make my to_replace variables case-incensitive?
If any more information is needed on this matter I will gladly provide so.
Kind regards,
JNeu
Somehow you know the patterns and the replacement values, yes? You need to impart that knowledge to your script.
The simplest way is to read it from a spreadsheet, e.g. on some sheet in some workbook, you have 1 column with the pattern, and another column with the replacement. Then you just read that data in (Range#getValues()), and then iterate that array to process your data range. Note that the pattern you store in the sheet should not include the literal constructor slashes, i.e. you'd want \d{1,3} and not /\d{1,3}/ in the cell.
Example:
function processAll() {
const source = SpreadsheetApp.openById("id of the spreadsheet with pattern - replacement data"),
info = source.getSheetByName("some sheet name")
.getDataRange().getValues();
const databook = SpreadsheetApp.getActive(),
sheet = databook.getSheetByName("name of the sheet with data to process");
if (!sheet) return; // sheet with that name doesn't exist.
const range = sheet.getRange(1, 1, sheet.getLastRow(), 1);
info.forEach(function (row) {
// Create case-insensitive pattern from the string in Column A, e.g. \d{1,3} and NOT /\d{1,3}/
var pattern = new RegExp(row[0], "i");
var repl = row[1]; // replacement text from Column B
replaceInSheet(sheet, range, pattern, repl);
});
}
Additional Reading:
RegExp
Array#forEach

using query() with apps script keeps adding 500 rows to sheet

I'm working on a large sheet and cells are precious given gsheets quota.
I have a range, and when data updates automatically a script updates the named range automatically to be the full length of the data.
The named range is called "gadatapull". This range is on the tab "datapull".
Two tabs. "datapull" is where fresh data is dumped and "data_prep" is where I do stuff to the data. After a fresh pull just now datapull has 2,733 rows of data, including the headers.
I would like data_prep to have the same length as datapull. Plus 7 rows for text at top of data_prep.
When my script to update data runs I do this:
// clear dataprep sheet for new data
var lastRow = 7;
var maxRows = dataprep.getLastRow();
if(maxRows - lastRow > 0) {
dataprep.deleteRows(lastRow+1, maxRows-lastRow);
}
data_prep has 7 rows (because the script just deleted all rows above 7).
Now, in data_prep cell A7 I have:
=query(indirect("gadatapull"),"select *")
Expected result was that all the fresh data in "gadatapull" would appear in data_prep tab and that data_prep tab would expand accordingly.
But what actually happens is all the data arrive as expected, but then there are an additional blank 500 rows at the bottom. This 500 number is too rounded off. Makes me think Gsheets is automatically adding this number as a default under some condition.
How can I prevent Gsheets from adding these additional 500 rows?
Instead of letting the sheet API expanding the number of rows (which is your hypothesis and might well be true ;-) you can add all the necessary cells before importing data.
I didn't try in real conditions but this should work.
Btw, the script imports data as well.
code :
function copyDataToSheet(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var dataprep = ss.getSheetByName('data_prep');
var datapull = ss.getSheetByName('datapull');
var lastRow = 7;
var maxRows = dataprep.getLastRow();
if(maxRows - lastRow > 0) {
dataprep.deleteRows(lastRow+1, maxRows-lastRow);
}
var datapullSize = datapull.getLastRow();
dataprep.insertRows(7,datapullSize);// insert exactly the number of rows you need.
var dataToCopy = datapull.getDataRange().getValues()
dataprep.getRange(7,1,dataToCopy.length,dataToCopy[0].length).setValues(dataToCopy);
}