Unable to get length of the non-string value in google script - javascript

I just have started coding in google script and stuck on this step of checking the cell is blank or not .length is working fine with string but not with non-string characters.
My google script Snapshot
My google sheet snapshot
Also some people suggested to use LEN function but I can't find any method "LEN" in google script. Please suggest I'm unable to get clear answer on internet.
Edit:
my exact code is here for better reference
const sheetName = "Sheet1";
const sheet =SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetName);
//var activeCellAddress = activeCell.getA1Notation();
function create_challan() {
check_range1=sheet.getRange(1,6).getDisplayValues()
check_range2=sheet.getRange(9,6).getDisplayValues()
if (check_range1.length == 0 && check_range2.length == 0) {
console.log(`Cell A6 is empty.`);
} else {
console.log(`Cell A6 is not empty.`);
}
}

When getValues is used, when the cell value is a number value, a date object, and a boolean value, the number, date object, and boolean (Object[][]) are returned, respectively. By this, when length is used, undefined is returned. I thought that this situation might be the reason for your current issue of but not with non-string characters..
In order to use length, in this case, how about using getDisplayValues instead of getValues? By this, all values are retrieved as the string value (String[][]). By this, length can be used for checking whether the cell value is empty. When this is reflected in a sample script, how about the following sample script?
Sample script:
function myFunction() {
const sheetName = "Sheet1"; // Please set your sheet name.
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetName);
const range = sheet.getRange("A2:A" + sheet.getLastRow());
const values = range.getDisplayValues();
for (let i = 0; i < values.length; i++) {
if (values[i][0].length == 0) { // or values[i][0] == ""
console.log(`Cell A${i + 2} is empty.`);
} else {
console.log(`Cell A${i + 2} is not empty.`);
}
}
}
When this script is used for your provided sample image when the cells are "A2" and "A6", Cell ## is not empty. is shown in the log. And, other cells show Cell ## is empty..
Note:
Above sample script uses multiple cells. When you want to check only one cell, getValue and getDisplayValue can be also used for one cell of the same situation. But, in this case, isBlank of Class Rnage might be useful. When this is reflected in a sample script, it becomes as follows.
function myFunction() {
const sheetName = "Sheet1"; // Please set your sheet name.
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetName);
if (sheet.getRange("A2").isBlank()) {
console.log(`Cell A2 is empty.`);
} else {
console.log(`Cell A2 is not empty.`);
}
}
References:
getValues()
getDisplayValues()
isBlank()

Related

Google Sheets Apps Script: if a neighboring checkbox is checked then copy and paste that cell to the next empty cell of another column

I am new to Google Sheets Apps Script, and I've tried dozens of scripts to make this work but haven't been successful. I'm trying to write a script that copies Topic Data from Column 'C' if a neighboring checkbox in Column 'B' is checked, and pastes that Topic Data into the next empty cell starting from the 5th row in Column 'A'. Any help will be greatly be appreciated! Thank you.
Sample Sheet:
https://docs.google.com/spreadsheets/d/1g9tn907Ve4rGFo7UI1NwYDBQqK5Y26TOSD_KnEfSWKw/edit?usp=sharing
This is my latest attempt:
function onEdit(){
var ss = SpreadsheetApp.getActive().getActiveSheet();
var selection = ss.getActiveCell().getA1Notation().split("");
var row = ss.getActiveCell().getRow();
//Gets the checkbox value
var checkBoxValue = ss.getActiveCell().getValue().toString();
if(selection[0] != "B") return;
switch(checkBoxValue){
case checkBoxValue = "true":
ss.getRange("C"+row).copyTo(ss.getRange('A5:A').getValues().filter(String).length + 5);
break;
}
}
I've been able to retrieve Data when a checkbox is checked but I can't figure out how to paste the Data into the next empty cell in Column 'A'. The script above was the latest iteration but this one in particular was one that I borrowed from other boards to see if I could adapt it...No Luck.
I believe your goal is as follows.
When the checkbox of column "B" is checked, you want to copy the value of column "C" to the next row of the last row of column "A".
From your showing script, you want to use onEdit.
In this case, how about the following modification?
Modified script:
When onEdit is used, the event object can be used. In this modification, the event object is used.
function onEdit(e) {
// Ref: https://stackoverflow.com/a/44563639
Object.prototype.get1stNonEmptyRowFromBottom = function (columnNumber, offsetRow = 1) {
const search = this.getRange(offsetRow, columnNumber, this.getMaxRows()).createTextFinder(".").useRegularExpression(true).findPrevious();
return search ? search.getRow() : offsetRow;
};
var sheetName = "Research";
var { range } = e;
var sheet = range.getSheet();
if (sheet.getSheetName() != sheetName || range.columnStart != 2 || !range.isChecked()) return;
range.offset(0, 1).copyTo(sheet.getRange(sheet.get1stNonEmptyRowFromBottom(1) + 1, 1), { contentsOnly: true });
}
In this case, please check the checkbox of column "B". By this, the script is run. When you directly run this script, an error occurs. Please be careful about this.
Note:
In the above modification, the 1st empty row is searched from the bottom. If you want to search it from the top, please use the following modified script.
function onEdit(e) {
// Ref: https://stackoverflow.com/a/44563639
Object.prototype.get1stEmptyRowFromTop = function (columnNumber, offsetRow = 1) {
const range = this.getRange(offsetRow, columnNumber, 2);
const values = range.getDisplayValues();
if (values[0][0] && values[1][0]) {
return range.getNextDataCell(SpreadsheetApp.Direction.DOWN).getRow() + 1;
} else if (values[0][0] && !values[1][0]) {
return offsetRow + 1;
}
return offsetRow;
};
var sheetName = "Research";
var { range } = e;
var sheet = range.getSheet();
if (sheet.getSheetName() != sheetName || range.columnStart != 2 || !range.isChecked()) return;
range.offset(0, 1).copyTo(sheet.getRange(sheet.get1stEmptyRowFromTop(1), 1), { contentsOnly: true });
}
If you want to uncheck after the value was copied, please add range.uncheck() to the last line of the script.
References:
Simple Triggers
Event Objects

Set value of event range does not recognize custom function that auto inputs a string in range

I have the below function which auto-fills column C with the word 'Text'
function emptycellfilling() {
var ss= SpreadsheetApp.getActiveSpreadsheet().getSheetByName('source_sheet');
var empty = ""
for(var i=2; i<=ss.getLastRow(); i++){
var val= ss.getRange(i,1).getValue();//select col num
if(val!=empty){ss.getRange(i,3).setValue(['Text']);}//select target col num and value you want to fill inside, here is 0
}
}
I have the below code to sync the two sheets
function ss1OnEdit(e){
if(e.range.getSheet().getName()!='source_sheet') return;
SpreadsheetApp.openById("1rGns0DGQbjlEpxTbdy1T_-m2oh7eK8tM9xyDzGqahJo").getSheetByName("target_sheet").getRange(e.range.getA1Notation()).setValue(e.range.getValue());
}
function createSS1OnEditTrigger() {
var ss=SpreadsheetApp.getActive();
var trgs=ScriptApp.getProjectTriggers();
var found=false;
for(var i=0;i<trgs.length;i++) {
if(trgs[i].getHandlerFunction()=="ss1OnEdit") {
return;
}
}
if(!found) {
ScriptApp.newTrigger("ss1OnEdit").forSpreadsheet(ss.getId()).onEdit().create();
}
}
However when the auto-fill function runs the String value 'Text' does NOT get copied to the other Sheet as it should unless the string value 'Text' was entered manually or copied by hand into column C.
Please advise on a better solution to have the .setValue(e.range.getValue()) recognize the auto-fill function values so they get copied to the other sheet(s).
Example Sheet:
https://docs.google.com/spreadsheets/d/1rGns0DGQbjlEpxTbdy1T_-m2oh7eK8tM9xyDzGqahJo/edit?usp=sharing
Thank you
Issue:
onEdit doesn't get triggered by data manipulation via script, it is triggered by changes made by user through the Google Sheets UI.
Proposed Solution:
Why not just write the same data to both sheets (source and target) when running auto fill?
Leave the trigger as it is for handling manual input (will still copy when manual input is done on source sheet)
Code:
function emptycellfilling() {
var ss= SpreadsheetApp.getActiveSpreadsheet().getSheetByName('source_sheet');
// target_sheet
var ss2=SpreadsheetApp.openById("1rGns0DGQbjlEpxTbdy1T_-m2oh7eK8tM9xyDzGqahJo").getSheetByName("target_sheet")
var empty = ""
for(var i=2; i<=ss.getLastRow(); i++){
var val= ss.getRange(i,1).getValue();//select col num
if(val!=empty){
ss.getRange(i,3).setValue(['Text']);
// write to target_sheet what you are writing in source_sheet
ss2.getRange(i,3).setValue(['Text']);
}//select target col num and value you want to fill inside, here is 0
}
}
Source vs target:
#NaziA
This code takes my input from child sheets and sets the value in the parent sheet:
function onEdit(e){
let ss = e.source.getActiveSheet();
let name = ss.getName();
if (name == "Sheet1" || e.range.rowStart == 1) return;
e.range.clearContent();
let db = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1");
let r = ss.getRange(e.range.rowStart,1).getValue();
db.getRange(r+1,e.range.columnStart).setValue(e.value);
}
Now the parent sheet is linked with another sheet as seen in the code above using openby setvalue, my main issue is that not only is the auto-fill not getting copied into the other sheet.
Rather every code input or values being set by a script into the parent sheet don't get recognized by the ss1OnEdit(e) which then writes these inputs to another shared Sheet.
I hope I have clarified my issue better, thank you so much for your effort though!

Apps Script doesn't compare 2 values in if-statement

I've created a new project that should compare a name from Sheet1 with a list of names in Sheet2 and check if the name is already in that list. For that I chose a for-loop to get through the list in Sheet2 and compare every list entry with the name from Sheet1. Only if the name already exists in the list stuff should happen.
function myFunction() {
var tabSheet1 = 'Sheet1';
var tabSheet2 = 'Sheet2';
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet1 = ss.getSheetByName(tabSheet1);
var sheet2 = ss.getSheetByName(tabSheet2);
var lastRow1 = sheet2.getLastRow() + 1;
var playerNameSheet1 = sheet1.getRange(1, 1).getValue();
for (var j = 1; j < lastRow1; j++) {
var playerNameSheet2 = sheet2.getRange(j, 1).getValue();
if (playerNameSheet2 == playerNameSheet1) {
...stuff...
}
}
}
Now my problem is that it seems like the script isn't able to identify that a name already exists in the list. Both values (playerNameSheet1 and playerNameSheet2) are completely identical (no space or other hidden obstacles), however the script would never continue with stuff in the if-statement. My example name to test my script was "Oliver Baumann".
I'm a bit confused about it - even more, because another comparison a bit later in the script code works just fine.
I've already tried to change the operator into === but that wouldn't work either.
if (playerNameSheet2 === playerNameSheet1) {
...stuff...
}
I've also observed that if I put a dot behind both variables I'm only able to choose further functions with playerNameSheet2, but not with playerNameSheet1. Maybe I did a typing error and am just too blind to see it? I don't know. Anyone an idea how to resolve the issue?
The complete project can be found here. However, a lot of stuff is in german and very rudimental. I just started it and haven't got time to clean it up. Just so you don't wonder.
You will likely benefit from a change to your inspection routine - currently what you have is not scalable due to the slow, repeated calls to the Spreadsheet Service. Use a batch method - getValues() - to return a Javascript Array that contains all the content you could want from your 'master list' of names:
// Create an N x 1 array of arrays, e.g. [ [r1c1], [r2c1], [r3c1], ... [rNc1] ],
// of data in column A in sheet2. There will be blanks at the end if other columns have more data.
var allNames = sheet2.getRange(1, 1, sheet2.getLastRow(), 1).getValues();
To check if the name from the first sheet is present, we can replace this code:
for (var j = 1; j < lastRow1; j++) {
var playerNameSheet2 = sheet2.getRange(j, 1).getValue();
if (playerNameSheet2 == playerNameSheet1) {
/* do stuff */
with this code (note j now starts at 0):
for (var j = 0; j < allNames.length; ++j) {
if (playerNameSheet1 === allNames[j][0]) {
/* do stuff */
If you only need to do stuff on a name once in the function call (e.g. you don't need to execute the loop body twenty times when the sheet 1 name is "Bob" and there are twenty instances of "Bob" on sheet 2), you can simplify checking allNames for a value with the Array#indexOf method. First, one must collapse the "2D" array of arrays of values into an array of values. We want to apply a function to every element of the outer array and construct an array of its outputs, so we choose to call Array#map on it:
var db = allNames.map(function (row) { return row[0]; });
The function we use simply returns the first element of the passed element - i.e. the value in the first column, resulting in an output like [ r1c1, r2c1, r3c1, ... rNc1 ].
The replacement code is then:
if (db.indexOf(playerNameSheet1) === -1) {
console.log({
message: "Did not find '" + playerNameSheet1 + "' in database.",
database: db, original: allNames, searched: playerNameSheet1
});
return;
}
/* do stuff */
Which says "if the name is not on sheet 2, log the failed lookup and then quit running the function." To promote actual logging, the log is sent to Stackdriver, which will keep it for much longer than the native Logger class would.
If your do stuff bits use the j index, you can still obtain that index and use the associated row in sheet 2:
var index = db.indexOf(playerNameSheet1);
if (index === -1) {
console.log({
message: "Did not find '" + playerNameSheet1 + "' in database.",
database: db, original: allNames, searched: playerNameSheet1
});
return;
}
/* do stuff with the user's existing row of data, e.g.
var userDataRow = sheet2.getRange(index + 1, 1, 1, sheet2.getLastColumn()).getValues();
var userData = userDataRow[0];
...
*/
A possible improvement to the indexOf modification, which I leave for you to investigate and/or implement, would be to use an Object to hold the names as "keys" (object properties) and the index of the associated sheet data (or even the data directly) as the associated value of the key-value pair.
you can try to convert data in array and compare in for-loop:
var dataRangeSpieler = sheetSpieler.getDataRange().getValues();
var dataRangeDBSpiele = sheetDBSpieler.getDataRange().getValues();
for (i in dataRangeSpieler ) {
for (j in dataRangeDBSpiele) {
if (dataRangeSpieler[i][1] == dataRangeDBSpiele[j][0]) {
Logger.log(dataRangeSpieler[i][1]); //Oliver Baumann
}
}
}

Google Sheets - reading values with 'com.'

I'm trying to loop through a column of a Google sheet and apply some formatting.
The data in column 2 looks like this:
name='com.android.dreams.basic'
name='com.android.bluetooth'
name='com.android.browser'
name='com.android.calculator2'
name='com.android.calendar'
name='com.android.camera2'
etc
For whatever reason, the data actually populating the "cell" variable is always "name='(class)'". I'm unsure why this isn't being read as the string that is actually in the cell. If "com.anything" is in the cell it will return '(class)'.
Thoughts? Below is my formatting function.
function ApplyFormatting() {
var currentSheet = SpreadsheetApp.getActiveSheet()
// Get the entire sheet range
var values = SpreadsheetApp.getActiveSheet().getDataRange().getValues()
for(var n = 0; n < values[0][0].length; n++){
var cell = values[n][2]
if (cell == "name='com.android.bluetooth'") {
currentSheet.GetRange(n +1, 1, 1, 1).setBagkgroundColor('green')
}
}
}
Looks like this is only an artifact of the debugger in google-apps-script. The actual values are correct even though the debugger shows incorrect values.
Thanks for your help Jack!

Google Script: Comparison of cells with === always gives out cells are different

I want to compare two cells and depending on whether they are equal or not send out different e-mails. After having read several articles on this topic I unfortunately do not see why the the following comparison with === always gives out as a result that the cells are different, even if I compare the same cells:
function SendEmail() {
var sheet = SpreadsheetApp.getActiveSheet();
var lastRow = sheet.getLastRow();
var ui = SpreadsheetApp.getUi(); // var file = SpreadsheetApp.getActiveSheet();
if (sheet.getRange(2,10) === sheet.getRange(2,10)) {
MailApp.sendEmail("ab#gmail.com", "test", "not equal!");
} else {
MailApp.sendEmail("ab#gmail.com", "test", "equal!");
}
}
It also does not work if I use !==.
Any hint is highly aprreciated - thanks!
You need to compare the values in the cells rather than the Ranges themselves.
If you are dealing with single cells, this is straightforward:
if(sheet.getRange(2,10).getValue() === sheet.getRange(2,10).getValue())
However if you want to compare ranges with multiple cells it is more complex, as you'll need to compare arrays of values.
I noticed you were comparing the same cells? I changed it to compare cells J2 & K2 and to log the differences. I hope this helps.
function SendEmail() {
var sheet = SpreadsheetApp.getActiveSheet();
var lastRow = sheet.getLastRow();
var ui = SpreadsheetApp.getUi(); // var file = SpreadsheetApp.getActiveSheet();
Logger.log(sheet.getRange(2,10).getValue());// logs value of J2
Logger.log(sheet.getRange(2,11).getValue());// Logs the value of K2
if(sheet.getRange(2,10).getValue() === sheet.getRange(2,11).getValue()) {
// MailApp.sendEmail("ab#gmail.com", "test", "equal!");
Logger.log('equal');
} else {
// MailApp.sendEmail("ab#gmail.com", "test", "not equal!");
Logger.log('not equal');
}
}
The return type for the getRange() method is Range, which inherits from Object. Objects are reference types and will never be considered equal unless the variables being compared point to the same object instance.
As mentioned, you need to call the getValue() method on the range that will return the contents of the cell and compare the values.

Categories