Lost zero number when appendRow - google-apps-script

When I use appendRow in Google Sheets, I lose the leading zero (when my data is a 'phone number), how can I keep it?
I tried getDataRange().setNumberFormat('#STRING#') before append but this did not work.
var rowdata = ["dinh","loc","09182734756"]
ws.appendRow(rowdata );
Here's an image of my current result:

Actually, setNumberFormat() works fine, but you need to apply it via setNumberFormat('#') or setNumberFormats([['#']]) (depends on whether you want to set it on one cell or on a custom Range).
Please, remember to set number formats before setting values to the target Range.
function testZero() {
var sh = SpreadsheetApp.getActiveSheet();
var v = ["dinh","loc","09182734756"]; //do not add the "'";
sh.appendRow(['']); //no need to do this step, it's an example;
var rng = sh.getRange(sh.getLastRow()+1,1,1,v.length);
rng.setNumberFormats([['#','#','#']]); //for test simplicity, set on Range you need to be of 0\d* pattern;
rng.setValues([v]);
}
UPD: credit goes to Tanaike - a more flexible (and less heavy) solution to accessing the last column needed is via the values Array length.

Related

Changing info on a different sheet in the same spreadsheet

I have two ranges of equal size on different sheets in the same spreadsheet. I am trying to find a row (based off of user input) in the first sheet and then use that index to modify a table in the second sheet that counts how many times that certain index has been used before (to make a nice looking pie chart).
This code runs but will not produce results on the second sheet. I've gone through the debugging process and my best guess is that for some reason, my for in loop is not running through. Attached is my code that takes in the beforementioned index and attempts to perform the second half of my goal.
function acceptToEncounterChart(ghostrow) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
SpreadsheetApp.setActiveSheet(ss.getSheets()[1]);
ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Average Encounter Chart");
var range = sheet.getRange("B3:B14")
for(var i in range) {
if(ghostrow == i) {
var before = range[i][0].getValue()
range[i][0].setValue(before + 1);
}
}
SpreadsheetApp.setActiveSheet(ss.getSheets()[0]);
};
Explanation:
I am not entirely sure what is your goal.
However, here is some fixes / improvements starting from the beginning:
You define 2 times the same variable ss with exactly the same value.
You don't need to set the active sheet, if your goal is to just get the sheet, therefore this line is redundant:
SpreadsheetApp.setActiveSheet(ss.getSheets()[1]);
Variable range is not an array but a range object. You can't index it and therefore you can't also use a for loop to iterate over a single object. For the same exact reason, the code inside the if statement is wrong, you can't index range. But you don't see any errors because the if statement evaluates to false.
In JavaScript and in many other programming languages, array indexes start from 0. Since your range starts from cell B3 or row 3, you need to use i+3 to match the data with the range.
For the same reason as the previous point, ghostrow is an index, not a row. The if statement compares an array index i with ghostrow, so ghostrow should not be confused with the actual sheet row. For example, if you choose ghostrow=5 then the current script will increment the value of the cell B8 (remember i+3) by 1.
Solution:
Here is a workable code snippet:
function acceptToEncounterChart(ghostrow) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Average Encounter Chart");
var data = sheet.getRange("B3:B14").getValues().flat();
data.forEach((v,i)=>{
if(ghostrow == i){
sheet.getRange(i+3,2).setValue(v+1)
}
});
ss.setActiveSheet(ss.getSheets()[0]);
}
Related:
Please explore the official google apps script documentation.

Google Sheet Custom Function Not Recaculating

Hi I have read about the caching issues with custom functions in Google Sheets, but I'm trying to understand why the following will successfully update if a cell is changed:
function doob(input){
return input * 2;
}
but this will not update:
function doob(input){
var sheet = SpreadsheetApp.getActiveSpreadsheet();
var range = sheet.getRange(input);
var values = range.getValues();
return values[0][0] * 2 ;
}
I need to get the range in order to use the .getBackgroundColor() of each cell.
Probably because in the first function you would pass the cellreference directly,
=doob(A1)
and in the script the value of that cell is used.
In the second, you would probably have to pass the range as string (since you want to get the backgroundcolors, so you are not after the values of that range, right ?)
=doob("A1:B8")
As you know, custom functions suffer from memoization. To work around that you could pass in the range a second time, without the quotation marks.
=doob("A1:B8", A1:B8)
That second paramater is a 'dummy' paramater as the script does nothing with it. BUT: any change in values in that range should make the custom function re-evaluate. However I don't know if that is gonna help you a lot if your final goal is to get the backgroundcolors.

Clear Invalid Values from Spreadsheet

I'm using Google Spreadsheets for this:
I have a spreadsheet which is basically a 4-week planner. Each day is divided into several slots, which can be assigned to any of our active clients. These cells have validation rules which reject invalid values.
The data that is permitted by the validation rules is sourced from a list on a separate sheet, which filters out clients when their status is changed from 'Active' to 'Cancelled', meaning they can no longer be assigned. The status is changed manually. Once an assigned client changes to 'Cancelled', it becomes an invalid client on the calendar.
Is there a way, using scripts, to find and clear the values of cells containing these invalid values? I've included a screen clipping below. The red corner is the invalid value.
I already have the onEdit trigger set up to run code, this will be calling a function to deal with this specific area.
screen clipping
Any help will be appreciated.
The code would look something like this:
function onEdit(e) {
//First check if you want the entire code to execute
if (myNeededCondtion !=== "theValueToMach") {
//End the code here
return;
}
var mySpreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var theSheet = mySpreadsheet.getSheetByName("name of sheet");
var arrayOfColumnValues = theSheet.getRange(row to start at, column to start at, numRows, numColumns).getValues();
var i=0;
var thisValue = "";
for (i=0;i<arrayOfColumnValues.length;i+=1) {
thisValue = arrayOfColumnValues[i][0];
if (thisValue==="Cancelled") {
//Set the cell value to a blank string
theSheet.getRange(i, column).setValue("");
};
};
};
You need to figure out what the range value parameters need to be, and edit the code. Add the correct sheet name to the getSheetByName method. Note that getValues() returns a two dimensional array. Each inner array represents a row. If you only get one column of data, then each inner array will only have one element in it. Arrays are indexed starting at zero.

setFormulas won't skip empty array elements, over-writes values

Consider the code below: it sets values then sets functions across a range, however it overwrites the values that were set.
function test() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var range = ss.getRange("A1:B2");
var values = [["First","row"],["Second","row"]];
var formulas = [["=42",""],["","=77"]];
range.setValues(values);
range.setFormulas(formulas);
}
Why is it that setting an empty formula will overwrite any set value and vice versa? Wondering if there is a way to skip setting an individual array element. I have tried 'undefined' and 'null with no luck.
Having to get the ranges of specific areas that require formulas for values to prevent overwriting of data adds an extra layer complexity to the script I'm writing.
Thanks for reading!
I think the problem can be largely mitigated by the fact that setValues() can be used to set formulae.
var range = ss.getRange("A1:B2");
var values = range.getValues();
// process the array, eg values[0][0] = "=42";
range.setValues();
This certainly explains why setting values overwrites formulas, but wouldn't it be nicer if there were a way to getValuesandFormulas together into an array?
If (like me) you were distracted into getValues and getFormulas, realise that you can use copyTo between ranges to copy the overall contents!

Google App script - setValues() doesn't work

So, I'm trying to write a script using the onEdit() event, which will basically remove links that are duplicates (technically, it removes everything, and only puts back things which aren't duplicates).
My code works fine all the way until it's time to write back non-duplicates. Namely, the line in which I use range.setValues(). I understand that it needs an array of arrays of cells which to edit, and that said array needs to fit in the range.
So far, I have :
if (unique)
{
newData.push(editedRow[0]);
Browser.msgBox(newData);
}
Unique is a variable I use that is false if an exact entry was found. With the msgBox command, I can verify that newData contains what it needs to contain. Further down, I have :
newDataFinal = [newData];
Browser.msgBox('Put values '+newDataFinal+' in range ' +range.getA1Notation());
range.setValues(newDataFinal);
To my knowledge, this should make NewDataFinal an array of arrays, which I can verify if I change setValues() to setValue(), which writes [[22.0, 13.0, 23.0]] (for my example) in the spreadsheet, which looks like an array of arrays to me.
The range should also match, since for this example, I get a prompt along the lines of "Put values 22,13,23 in range B2:B4" from the msgBox, which seems as a fitting range.
So, what am I doing wrong?
Here's the rest of the code (please excuse the abundancy of comments/msgboxes and lack of elegancy, the priority is to get it to work, I can probably optimize it and clean it up a bunch afterwards) :
function onEdit(e)
{
var range = e.range;
var values = range.getValues();
var sheet = SpreadsheetApp.getActiveSheet();
if (sheet.getName() != 'testiranje') return;
newData = new Array();
// Browser.msgBox(range.getA1Notation());
range.clear();
var data = sheet.getDataRange().getValues();
var counter = 0;
for (editedRowIndex in values)
{
unique = true;
editedRow = values[editedRowIndex];
// Browser.msgBox('Edited Row ' +editedRow);
for(i in data)
{
var row = data[i];
// Browser.msgBox('Old Row '+row);
for (j in row)
{
// Browser.msgBox(row[j] + ' vs ' + editedRow[0])
if (editedRow[0] == row[j])
{
Browser.msgBox('Hit! '+editedRow[0]);
unique = false;
}
}
}
if (unique)
{
// Browser.msgBox('Pushing '+editedRow[0]+' in newdata');
newData.push(editedRow[0]);
Browser.msgBox(newData);
}
}
newDataFinal = [newData];
Browser.msgBox('Put values '+newDataFinal+' in range ' +range.getA1Notation());
range.setValues(newDataFinal);
// range.setNote('SCIENCE');
}
I didn't test your code because I didn't feel like creating a sheet for it but what I can suggest (that should solve this issue in any case) is to replace your range.setValues(newDataFinal); with this :
sheet.getRange(range.getRowIndex(),range.getColumnIndex(),newDataFinal.length,newDataFinal[0].length).setValues(newDataFinal);
And if you want to know why the range and array didn't fit you can use this code :
(I used Browser because you seem to like it... I prefer Logger.log)
Browser.msgBox('data height = '+newDataFinal.length+', data width = '+newDataFinal[0].length+' and range height is '+range.getHeight()+', range width is '+range.getWidth()+' ... does it fit ?');
Note : I'm almost sure that your initial range is bigger than the newData array since you remove elements from the initial data... My best guess would be that heights don't fit. (but that's only a guess ;-) since you didn't mention the error message you get...)
the problem is that you cant change cells from an onEdit handler. see the docs. instead install your own onEditCustom handler.