GAS OR operator makes both tests ignored - javascript

I have this function looping through all sheets, and if the sheet is not hidden, add the sheet name to the array out.
function sheetnames() {
var sheets = SpreadsheetApp.getActiveSpreadsheet().getSheets();
var out = new Array()
for (var i=0 ; i<sheets.length ; i++)
if (sheets[i].isSheetHidden()!= true){
out.push( [ sheets[i].getName() ]
)}
Logger.log(out);
}
I would also like to test for specific sheet names, which I am able to do with
if (sheets[i].getSheetName()!= 'Sheet1'){
However when I put them together with || an OR operator, both tests are ignored.
if (sheets[i].isSheetHidden()!= true || sheets[i].getSheetName()!= 'Sheet1'){
I'm not sure if it is the way I am handling || or something else I'm not seeing.
In this example, sheet1 is visible, so would pass the first part of the test.

(sheets[i].isSheetHidden()!= true || sheets[i].getSheetName()!= 'Sheet1')
will return true if the current sheet is not hidden or not named 'Sheet1'. Or in other words, it will only return true if the current sheet is named 'Sheet1' and is hidden. That's probably not what you want, is it? Perhaps what you're looking for is the && AND logical operator?
Also, I'd suggest you look into formatting code, why you should use !== instead of !=, and see this question for information on using var a = []; compared to var b = new Array();

Related

While loop not stopping when 2D array cell isn't defined (javascript)

Goal: Run through the columns of a 2D array (comes from an Excel file with uneven column lengths) and put the entries that exist into their own array.
What I did: The length of the longest column is 90 entries, which is the second column in the Excel file, and the shortest is 30, which is the first column. I set up a for loop to go through each column and a while loop to go through each entry while it exists and append it to a new array.
Original(ish) Code:
//read in Excel file into 2D array called "myExcel"
var columnNames = ["shortest", "longest", "irrelevant"];
shortArray = [];
longArray = [];
irrArray = [];
var s
for (var i = 0; i < columnNames.length; i++) {
var columnName = columnNames[i];
s = 0;
while (myExcel[s][columnName]) {
if ((columnName === "shortest")) {
var row = myExcel[s][columnName];
shortArray.append(row);
s++;
} else if ((columnName === "longest")) {
var row = myExcel[s][columnName];
longArray.append(row);
s++;
} else if ((columnName === "irrelevant")) {
var row = myExcel[s][columnName];
irrArray.append(row);
s++;
}
}
}
Problem: It's only half working. It makes it through the first column (30 rows) just fine--it stops when myExcel[s][columnName] no longer exists (when columnName = "shortest" and after s = 29). Then, it makes it all the way through columnName = "longest" and s = 89 before giving me the error "TypeError: Cannot read property 'longest' of undefined". I'm assuming it's because it's trying to go through row 90, which doesn't exist. But I thought that's where my while loop would stop.
What I've Tried:
do while loop
//blah
do {
//blah
} while (myExcel[s][columnName]);
Added additional while loop condition
//blah
while ((myExcel[s][columnName]) && s<myExcel.length) {
//blah
}
Using typeof
//blah
while (typeof (myExcel[s][columnName]) === 'string') { //also used this with !=='undefined' and ==='string' when I added a number to the end of each row in the Excel sheet
//blah
}
And basically every combination of these (and probably much more I'm forgetting). I'm sure it's an easy fix, but I've spent days trying to figure it out so I guess I have to ask for help at this point. I'm also a MATLAB person and recently had to learn both Python and Javascript because of COVID, so it could possibly be a language switch issue (although I don't think so because I've been googling and messing with this for days). Any help would be very appreciated!
In your while loop change the check to,
while(myExcel[s] && myExcel[s][columnName] ) {
If you are writing modern Js, then you could simply optional chain it like so, while(myExcel[s]?.[columnName])
The thing is, you are trying to traverse inside an outer array. But you first need to check if outer array exists and then go check the inner array.
I don't fully understand you approach, but I think you're looking for this:
var shortArray = [];
var longArray = [];
var irrArray = [];
for(let row of myExcel){
if(!row) continue; // not sure if this check is necessary.
if(row.shortest) shortArray.append(row.shortest);
if(row.longest) longArray.append(row.longest);
if(row.irrelevant) irrArray.append(row.irrelevant);
}

compare array values javascript replace values matched

I have two sheets, one is updated from time to time, one is the backup which it may contain some the same values of the first one, the first one needs to be updated in some columns with recorded data already saved in the backup, I have written this script which is working fine some times but other times completely wrong (!) is taking right value form wrong row! second sheet is sorted descendant way so that inedxOf will match the right value for sure as first one.
var M = data.length;
for ( var r = 0; r < M ;r++){
var L = datiArchivio.length;
var row = data[r];
var nave = row[4];
var imo1 = data [r][23]
if ( imo1 == "" && nave!= ""){
for (var j = 0; j < L;j++){
var roww = datiArchivio[j];
var IMO = roww[23];
var naveArchivio = roww[4];
var GT = roww[25];
if ( IMO == ""){continue;} else {
if ( naveArchivio == nave) {
row.splice(23,1,IMO);
row.splice(25,1,GT);
}
}
}
}
}
I guess you meant something like this, but it is not working, in the archivio sheet there could be matching row with empty value at the reference column as well as fulfil values, it is ordered so that it should match first one which fullfil the condition, hence I added roww[23]=="" condition in order to skip that j row. it is challenging, keep on work on the logic.
This last version is working, and also quickly, I was stucked in the old "rubbish in rubbish out".

Optimizing for loops in google apps script

I am using for loops to search through large sheets (approximately 4500 rows).
Most of them look something like this:
function lookup(value) {
var tables=SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Name");
var columnvalues = tables.getRange(1, 1,tables.getLastRow()).getValues();
var searchResult = columnvalues.findIndex(value); //Row Index - 1
}
Array.prototype.findIndex = function(search){
if(search == "") return false;
for (var i=0; i<this.length; i++)
if (this[i].toString().indexOf(search) > -1 ) return i;
return -1;
}
The app script currently runs relatively slowly. I am looking for a way to either speed up my current code or for another search method. I've been thinking about using the google spreadsheet lookup functions (index-match, vLookup) but I haven't found a way to access those functions in apps script. Any thoughts?
currently you wont find a way to do this faster with apps script. you have to get the entire column and search one by one like you are already doing.
the alternative you mention using sheet formulas can be flacky as you might find that the cell formulas dont inmediately update results as you change values.
the only case i can see this being sped up is if you need to make multiple search calls. in that case it will be faster to pass search terms as an array and search them all at once in the single loop.
As you are searching a single column something a bit simpler might be a bit faster.
function lookup(value) {
var searchResult = false;
if(value == "") return;
var tables=SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Name");
var columnvalues = tables.getRange(1, 1,tables.getLastRow()).getValues();
for (var i=0; i<columnvalues.length; i++) {
if(columnvalues[i][0] == value) {
searchResult = i; //Row Index - 1
break;
};
}
The fastest way that I have found of searching through an array is using the .filter() method of an array.
var yourData = range.getValues();
var results = yourData.filter(function(val) {
return val[0] == search ||
val[0].includes(search);
});
//now you have a 2d array of results
Modify that such that it is the range you want(sorry, on mobile) and that is the fastest way that I found. Avoiding the For-Loop is the best. You could also use a .forEach()

Setting a Javascript if statement with 2 requirements to one line

var status = result.locations[index].status;
var operator = result.locations[index].operator;
var original = result.locations[index].original;
var produced = result.locations[index].produced;
var href = result.locations[index].more;
I have the above which each need to be an if statement to check if there is content and my output is the below code.
if (result.locations[index] && result.locations[index].status){
var status = result.locations[index].status;
} else {
var status = '';
}
I would need to reproduce this per line from the code at the top of the post. What would be the best method to simplify each down to keep the code neater and not produce 5 lines of if statement when 1 or 2 would do.
var status = (result.locations[index] && result.locations[index].status ? result.locations[index].status : '');
Not sure why you want to, but:
var status = (result.locations[index] && result.locations[index].status) ? result.locations[index].status : ""
Your problem is trying to access a property of a "deep" javascript object using its path.
This is a common question :
Javascript: Get deep value from object by passing path to it as string
Accessing nested JavaScript objects with string key
There is no built-in way to do this in javascript.
There are plenty of libraries to do that, for example, with selectn, this would become something like (I have not tested it, so I don't know if the index part will work, but you get the idea) :
var status = selectn("locations." + index + ".status", result) || ''
If the structure of your objects is always the one above (that is, the property is just at one level of depth), and you're not expecting 'falsy', you could simply write the 'test' function yourself :
function safeGet(instance, propertyName, defaultValue) {
// As pointed by AlexK, this will not work
// if instance[propertyName] can be anything Falsy ("", 0, etc...)
// If it's possible, get a library that will do
// the full series of insane checks for you ;)
if (instance && instance[propertyName)) {
return instance[propertyName];
} else {
return defaultValue;
}
}
var location = result.locations[index]; // Potentially undefined, but safeGet will deal with it
var status = safeGet(location, "status", "");
var operator = safeGet(location, "operator", "DEFAULT_OPERATOR");
...
var status = result.locations[index] && result.locations[index].status || '';
However, better maje sure before, if result.locations[index] exists... else do whatever is to be done in your code..

String control in loops

I have a big question.
I have many Strings in my Programm and want to check these Strings on there values.
I wrote a Loop for it, but insted of the Definition of an String he is creating a new value. It's basicly really difficult to discribe, also because i am basicly German.
But i can give you my current code, so maybee you will see what I mean:
{
var Loch1G = $('#m1-Rundenanalyse-Datum').val(); //In the strings just the number is changing
var Loch2G = $('#m1-Rundenanalyse-Turnier').val();
x=1
while (x <= 2) {
if ("Loch" + x + "G" == ""){ //Next String is genrated (x=x+1)
alert("Eingabe war leer");
}
x=x+1
}
}
How can I solve this?
I'd suggest using an array to store the values you want to check:
var lochs = [];
lochs.push($('#m1-Rundenanalyse-Datum').val());
lochs.push($('#m1-Rundenanalyse-Turnier').val());
for (var i = 0, len = lochs.length; i < len; i++){
if (lochs[i] == ''){
alert("Eingabe war leer");
}
}
JS Fiddle demos: passes (no alert), fails (alert)
This suggestion is based on my presumption that you're trying to create the names of the vars you want to check, which won't work, whereas this approach lets you store all values (however many) in the same array and then iterate over that array to find any values that are equal to an empty string.
If you really want to stick with your current approach, you could do the following:
{
window.Loch1G = $('#m1-Rundenanalyse-Datum').val(); //In the strings just the number is changing
window.Loch2G = $('#m1-Rundenanalyse-Turnier').val();
var x=1;
while (x <= 2) {
if (window["Loch" + x + "G"] == ""){ //Next String is genrated (x=x+1)
alert("Eingabe war leer");
}
x=x+1;
}
}
But I can't think why you'd want to; plus the use of global variables is poor practice as it explicitly makes those variables available to every closure within the document, which allows them to be easily, and accidentally, overwritten.
In a reasonably up-to-date browser, that implements Array.prototype.every, you could dispense with the explicit iteration:
var lochs = [];
lochs.push($('#m1-Rundenanalyse-Datum').val());
lochs.push($('#m1-Rundenanalyse-Turnier').val());
if (!lochs.every(function(a){ return a !== ''; })) {
alert("Eingabe war leer");
}
JS Fiddle demos: passes (no alert), fails (alerts).

Categories