Filter function will not delete my empty rows - Google App Script - javascript

I want to import rows from one google sheet to the other, however source sheet imports a number of empty rows. Now I use a filter function to get rid of these rows but they will not disappear, can anyone tell me why?
var a = SpreadsheetApp.openByUrl("url").getSheetByName("Admin Use Only").getRange(4,1,6,21).getValues();
var b = SpreadsheetApp.getActive().getSheetByName('Credit_Detail');
b.getRange(b.getLastRow() +1, 1, a.length,21).setValues(a);
//filter function below:
var otarget=b.getRange(2,1,b.getLastRow()-1, 26).getValues();
var data=otarget.filter(function(r){
return !r.every(function(cell){
return cell === "";});
});
Logger.log(data);
b.getRange("A2:Z").clearContent();
b.getRange(3,1,data.length,data[0].length).setValues(data);

here's how I would do it. First, create an variable to store the array of the source. then run a for loop scanning the first column for empties. something like: for (var i = 0, i < data.length; i++) { if (data[i][0] != '') { XXXX } }
XXXX means that you can either put a code to create a new set of array which can be passed to the target sheet at once or use append row to transfer non blank rows to the target sheet one by one.
Note: Creating a new array to store non-empty rows would speedup the execution time if you are dealing with large data, thousands of rows.

Related

Google Apps Script Better Way to Get Unique Values

I have working code that takes data from two non-adjacent columns in a Google Spreadsheet, looks for unique values in the first column, and if unique creates a new array with the unique value from the first column and corresponding value in the second column. The problem is, the data I am using is already somewhat long (413 rows) and will only get longer over time. It takes about 1-2 minutes for the code to run through it. I've been looking for a shorter way to do this and I've come across the filter() and map() array functions which are supposedly faster than a for loop but I can't get them implemented correctly. Any help with these or a faster method would be greatly appreciated. The code I have right now is below.
function getkhanassignments(rows) {
var assignmentsraw = [];
var temparray = [];
var previousassignment = datasheet.getRange(50,1).getValue();
for(i=0, j=0;i<rows-1;i++) {
if(datasheet.getRange(50+i,1).getValue() != previousassignment) {
previousassignment = datasheet.getRange(50+i,1).getValue();
assignmentsraw[j] = new Array(2);
assignmentsraw[j][0] = datasheet.getRange(50+i,1).getValue();
assignmentsraw[j][1] = datasheet.getRange(50+i,8).getValue();
j++;
}
}
Logger.log(assignmentsraw);
return assignmentsraw;
}
The answers I've found elsewhere involve just getting unique values from a 1d array whereas I need unique values from a 1d combine with corresponding values from another 1d array. The output should be a 2d array with unique values from the first column and their corresponding values in the second column.
Solution:
The best practice of looping through ranges in Google Apps Script is to dump the range values into a 2D array, loop through that array, and then return the output array back to Google Sheets.
This way, there would be no calls to Sheets API inside loops.
Sample Code:
function getkhanassignments(rows) {
var assignmentsraw = [];
var table1 = datasheet.getRange(50,1,rows).getValues();
var table2 = datasheet.getRange(50,8,rows).getValues();
var previousassignment = table1[0][0];
assignmentsraw.push([table1[0][0],table2[0][0]]);
for(i=0; i<rows; i++) {
if (table1[i][0] != previousassignment) {
assignmentsraw.push([table1[i][0],table2[i][0]]);
previousassignment = table1[i][0];
}
}
Logger.log(assignmentsraw);
return assignmentsraw;
}
References:
Class Range
push()

Google Scripts how to delete extra rows from an array

I am working on a google sheets template that will have some roster maintenance built in. When rosters are updated on the main "roster" tab, I would like for all the other tabs in the sheet to check student ID #s against the updated roster tab. In the code, an example sheet is "anet" sheets the sheets. I am using indexOf and a for loop to check each value in the "anet" sheet against the IDs in the "roster" sheet. If an ID# has been removed from the "roster" sheet, I would like that row to be deleted in the "anet" sheet.
When I run the script right now, some of the rows are deleted, but not all of them. The list of IDs begins in A3 on the "roster" tab, and the other list begins in A15 on the "anet" tab. Can someone help me understand why it is deleting some of the rows returning an indexOf of -1, but not all of the rows I need deleted?
function withdrawnStudent (){
let lastRowTyler = roster.getLastRow();
let tylerData = roster.getRange(3,1,lastRowTyler,1).getValues();
let tylerArray = tylerData.map(function(r){ return r[0]});
let anetLastRow = anet.getLastRow();
let anetLastColumn = anet.getLastColumn();
let anetData = anet.getRange(15,1,anetLastRow,anetLastColumn).getValues();
let anetIDArray = anetData.map(function(r){ return r[0]});'''
for (let index = 14; index < 200; index++){
if(tylerArray.indexOf(anetIDArray[index][0]) === -1){
anet.deleteRow(index +14);
Logger.log(tylerArray.indexOf(anetIDArray[index][0]))
Here is a link to an example spreadsheet. In the "roster" tab, it lists 4th grade student IDS. In the "anet" tab, all rows with a number should be deleted because these are 5th grade IDs. However, not all rows are getting deleted, only some.
https://docs.google.com/spreadsheets/d/1vDse6X6gs3bkgnlBfgo-vzERkAMud3rUDC6j8fEkcrk/edit#gid=447751616
So when the document changes, set up a trigger to fire your script, and your script will loop through all the available IDs in the first sheet and save them to an array. Then in your second sheet, you will loop through the IDs, and if it is not in the array, then delete the row. We want to make sure that we run the loop backward because if we delete rows and keep moving down, the chart will be skipping rows here and there since the table has shifted upwards.
Here's what I was able to come up with:
function withdrawStudent() {
//Get Student IDs From Roster Spreadsheet
var rosterSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Roster");
var dataRangeOnRosterSheet = rosterSheet.getDataRange();
//Returns a nested Array of all values in the 3rd row, 1st column, all the rows to the end, only one column
//I added the flat() to make it into a one-dimenstional array
var studentIDs = rosterSheet.getRange(3, 1, dataRangeOnRosterSheet.getLastRow() - 1, 1).getValues().flat();
Logger.log(JSON.stringify(studentIDs)); //If you want to see what the data looks like
//Now loop through each student ID in the second sheet, and if it doesn't exist in our first array then delete the row
var ANetSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("ANet");
var dataRangeOnANetSheet = ANetSheet.getDataRange();
var lastRow = dataRangeOnANetSheet.getLastRow();
var firstRow = 15;
//Reverse the for loop to work bottom-up because row deletion shifts the chart
for (var i = lastRow; i >= firstRow; i--) {
var currentStudentID = ANetSheet.getRange(i, 1, 1, 1).getValue(); //Get Student ID of current row
//If the currentStudentID is not found in our list of student IDs, remove it
if (!studentIDs.includes(currentStudentID)) {
//Remove the row
ANetSheet.deleteRow(i);
}
}
}
How to set up your trigger so that it runs your function every time a user edits the chart:
Disclaimer: I made a copy of your document so I could test my code and make sure it works, but I'm deleting it now. Hope you are fine with that!

Automatically separate and delimit a string in Google sheets

Google sheets has an option which is selectable from the top menu to separate text into columns when specifying a character. It's possible use a comma or other characters.
I am looking for a script which can do this process automatically for a given column. There are numerous scripts available to do this but I have not been able to accomplish my task using them.
I am using an application on Android which allows me to scan a qr code and send the string of information to Google sheets.
A sample of the information would appear as : 464839|362|2840|927|72|617
I need to separate this information into separate columns when the information is sent to sheets. The process should be automatic.
I have a snip of code which I've found searching however it doesn't work for me.
var range = SpreadsheetApp.getActiveRange();
var cell = range.getCell(1, 1); // Gets the cell A1
var cellValue = cell.getValue(); // Gets the value of the cell A1
var cellArray = cellValue.split("|"); // Splits the cell value by ',' and stores it in array.
//Iterate through the array
for(var i = 0; i < cellArray.length; i++){
Logger.log(cellArray[i]);
}
I'm not very code savvy, please help.
Below would be a code that you place on an installable trigger that runs at a regular interval and iterates through the values of each row in column A and tries to split the value with the pipe symbol and the replace then write those split values along the columns in that row. If this has already been done in the past the function will error when it tries to split the values because no pipe symbol exists but the try/catch will catch that error and allow the function to continue through the loop.
function myFunction() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("NameOFSheet"); //Change "NameOFSheet to the actual name of the sheet.
var col1Values = sheet.getSheetValues(1, 1, sheet.getLastRow(), 1) //Gets 2d (2 dimensional) array of the Values in Column 1 (getSheetValues(startRow, startColumn, numRows, numColumns)_
var splitVals = []; //Instantiate an empty 1d array variable
//Iterate through the 2d array and set the values
for(var i = 0; i < col1Values.length; i++){
try{
splitVals = col1Values[i][0].split("|"); //Creates a 1d array of values split at every occurrence of the pipe symbol ("|").
//If there is no pipe symbol (which would be the case if this operation has already happened then the array will be blank because .split will throw an error which will get "caught" in the try catch and the row will be skipped
//Iterate over the splitVals array and set the values of the columns along the row (i+1) that you are in.
for(var col = 0; col < splitVals.length; col++){
sheet.getRange(i+1, col+1).setValue(splitVals[col])
}
}catch(e){}
}
}
I commented the code for explanation. I would recommend reading up on 2 dimensional arrays to help you understand them and the code above better.

Data Scraping With ImportHTML in Apps Script & Google Sheets

Goal: I am trying to pull data from a website and use it to create a big table. I can tell that I'm very close to getting this to work, but I've reached a roadblock.
Background:
I have a google sheet with three pages. (1) Titled "tickers" is a list of every ticker in the S&P 500, in rows A1-A500. (2) Titled actionField is just a blank page used during the script. (3) Titled resultField will hold the results. The website I am pulling from is (http://www.reuters.com/finance/stocks/companyOfficers?symbol=V) Though, I want the script to work (with minor modification) for any data accessible through importHtml.
Script:
The script I currently have is as follows:
function populateData() {
var googleSheet = SpreadsheetApp.getActive();
// Reading Section
var sheet = googleSheet.getSheetByName('tickers');
var tickerArray = sheet.getDataRange().getValues();
var arrayLength = tickerArray.length;
var blankSyntaxA = 'ImportHtml("http://www.reuters.com/finance/stocks/companyOfficers?symbol=';
var blankSyntaxB = '", "table", 1)';
// Writing Section
for (var i = 0; i < arrayLength; i++)
{
var sheet = googleSheet.getSheetByName('actionField');
var liveSyntax = blankSyntaxA+tickerArray[i][0]+blankSyntaxB;
sheet.getRange('A1').setFormula(liveSyntax);
Utilities.sleep(5000);
var importedData = sheet.getDataRange().getValues();
var sheet = googleSheet.getSheetByName('resultField');
sheet.appendRow(importedData)
}
}
This successfully grabs the ticker from the tickers page. Calls importHtml. Copies the data. And appends SOMETHING to the right page. It loops through and does this for each item in the ticker list.
However, the data being appended is as follows:
[Ljava.lang.Object;#42782e7c
[Ljava.lang.Object;#2de9f184
[Ljava.lang.Object;#4b86a4d0
That displays across many columns, for as many rows as there are iterations in the loop.
How do I successfully append the data?
(And any advice on improving this script?)
The appendRow method is not suitable here. As it only appends one row, its argument is expected to be a 1D array of values.
What you get from getValues is normally a 2D array of values, like [[a,b], [c,d]]. Even if it's just one row, getValues will return [[a,b]]. The only exception is a single-cell range, for which you get just the value in that cell. It's never a 1D array.
If just one row is needed, use, e.g., appendRow(importedData[0]).
Otherwise, insert the required number of rows and assign the 2D array of values to them.
var sheet = googleSheet.getSheetByName('resultField');
var lastRow = sheet.getLastRow();
sheet.insertRowsAfter(lastRow, importedData.length);
sheet.getRange(lastRow + 1, 1, importedData.length, importedData[0].length)
.setValues(importedData);

Problems with code listing unique items in a list and copying them into new worksheet

I'm trying to run the following code on a spreadsheet. The column of names is the 4th column. I'm attempting to run through that list of names, pushing each new unique name to an array (listOfNames), and then add a new worksheet (NamesList), and finally add the array to cell A1 of the new worksheet. When I run the code below, all I get is a blank popup with an OK and Cancel button. When I view the log, it is blank as well. I'm quite new at this, and I feel like I'm missing something obvious... just not sure what it is. Am I misunderstanding something specific to GAS rather than JS?
var sheet = SpreadsheetApp.getActiveSheet();
var listOfNames = new Array ();
function copyNames() {
var data = sheet.getDataRange().getValues();
for (i=0; i<=sheet.getLastRow(); i++){
var tempName = sheet.getDataRange(i,4).getValue();
for (i=0; i<=listOfNames.length; i++){
if (tempName != listOfNames[i]){
listOfNames.push(tempName);
logger.log(listOfNames);
}
}
}
sheet.insertSheet(ListOfEDs);
sheet.getRange('a1').setValue(listOfEDs);
}
Edit: I'm starting to see that this will push values multiple times to the list... so maybe it's just back to the drawing board all together. I have found other code that would create a list of unique elements, but wasn't really sure how that code worked. I thought I'd try to figure it out myself so I'd at least understand it.
EDIT 2: Ok... I tried some new code, but I'm still getting a blank message box, and nothing on the log. I wasn't sure if having i be the iterator for a for loop within a for loop was a bad thing, so I switched it to j. Also, I know there's the remove duplicates example, and have been looking at that, but am unsure why one needs to use join.
function copyNames() {
var sheet = SpreadsheetApp.getActiveSheet();
var listOfNames = new Array ();
var data = sheet.getDataRange().getValues();
for (i=2; i<=data.length; i++){ //starting at 2 because first row is a header
var tempName = data[i][4];
for (j=0; j<=listOfNames.length+1; j++){
if (tempName != listOfNames[j]){
listOfNames.push(tempName);
logger.log(listOfNames);
}
}
}
sheet.insertSheet("ListOfNames");
sheet.getRange('a1').setValue(listOfNames);
}
As far as I understand you went help instead of a ready solution, so here you go.
First:
Try to avoid using global variables. Keep all your variables inside of your functions. Otherwise you will have issues when you add more functions to your spreadsheet. Your Code should start like this:
function copyNames() {
var sheet = SpreadsheetApp.getActiveSheet();
var data = sheet.getDataRange().getValues();
var listOfNames = new Array ();
Second:
Try to decrease the amount of service calls to spreadsheet. it takes several seconds for your script to go to your sheet and take the values. Instead of doing it each time, take these values once and push them in to a variable.
In your case, you already did that, however did not use that variable and kept using service calls. Check this line:
var data = sheet.getDataRange().getValues();
Now all of the data on that sheet is in a variable called data.
sheet.getDataRange(i,4).getValue();
is the same as
data[i][4];
The only difference is that in the first case it will take approximately 2 seconds to get that value, while in the second case only a few milliseconds.
Same goes for
sheet.getLastRow();
Either call it once and push it into a variable and use that, or in your case just use
data.length;
Third:
listOfNames is an empty array, so it's length is 0.
This line
for (i=0; i<=listOfNames.length; i++)
will not even run, as both i and listOfNames.length is 0.
That is why you logger does not give any output. Your script never get's to that line.
Fourth:
You do not have a variable called ListOfEDs, therefore your last two rows of code just give an error. Your script does not know what is ListOfEDs as it doesn't exist.
Hope this helps.

Categories