Javascript on Google Sheets - Get Column Number from Prompt Input - google-apps-script

I have two sheets on a Google Spreadsheet. One has a lot of information and references and the other has the same reference in the first cell of a column with link names and links below. I am trying to get around the "no multi-hyperlinking in one cell" limitation by having the user input the reference they want to search and then searching through the second sheet to find the reference and have a pop-up box with the links.
So far, I am able to get the links from the second spreadsheet column and display them in a pop-up box with this code:
function main(){
var column = SearchAndFind()
showURL(getLinks(column))
}
function getLinks(col){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sh = ss.setActiveSheet(ss.getSheets()[1]);
var cell = ss.getActiveCell();
var values = sh.getDataRange().getValues();
var myArray = []
for(n=1;n<values.length;++n){
var cell = values[n][col] ;
myArray.push(cell);
}
return myArray;
}
function showURL(data){
var app = UiApp.createApplication().setHeight(40+8*data.length).setWidth(200);
app.setTitle("Show URLs");
var panel = app.createVerticalPanel();
app.add(panel);
for(var d=0 ; d<data.length;d=d+2){
var link = app.createAnchor(data[d],data[(d+1)]);
panel.add(link);
}
var doc = SpreadsheetApp.getActiveSpreadsheet();
doc.show(app);
return;
}
When I hard-coded a random column number to the getLinks function and it worked fine but I need to be able to get the column number from a user search of the first cell in each column in the second sheet.
This is the code I have right now that doesn't work:
//I know that it will always be the second sheet on the spreadsheet
//Search the column headers on the second sheet
//When one matches, return the index
function SearchAndFind(){
//Make the 2nd sheet active
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sh = ss.setActiveSheet(ss.getSheets()[1]);
var range = sh.getRange(1, 1, sh.getMaxRows(), 1);
var values = range.getValues();
//Get the user input for the text they want to search
var ui = SpreadsheetApp.getUi();
var search = ui.prompt('Enter the ID: ');
var searchString = search.getResponseText()
//for loop to iterate through the first row and find the matching cell
//return the index of that column
for (n = 0; n < values[0].length; n++){
var cell = values[0][n]
if (cell === searchString){
return n;
}
}
}
When I run all of the code (including the function SearchAndFind that doesn't work), the pop-up box comes up with undefined, linking nowhere. Admittedly, I don't have a lot of experience with Javascript so I think I just don't understand it well enough to find the bug here.

You are pulling only one column and then checking for the match in columns.
var range = sh.getRange(1, 1, sh.getMaxRows(), 1);
Gives you only the first column. And
for (n = 0; n < values[0].length; n++){
Looks through those columns. So your values[0].length is 1, and your loop only runs once.
Are you trying to loop through all rows form first column, or through first row of all columns.
Also you should change your loop to
for (var n = 0; n < values[0].length; n++){

Related

Google Script - Hide column if a cell within the column contains a specific value

I am trying to hide all columns in Google Sheets if a cell within that column contains the letter "S", I am checking row 6 which has the initials of each day of the week and want the ability to show and hide the weekends
Columns A6:G6 have M,T,W,T,F,S,S
Reason for the 9999 is due to this sheet containing multiple weeks and I am trying to look through all of them.
function Hide() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var data = sheet.getDataRange();
var lastCol = data.getLastColumn()+1;
for(var i = 1; i < lastCol - 9999; i++) {
if(data.getCell(6, 9999 + i).getValue() === "S") {
sheet.hideColumns(9999 + i);
}
}
};
This is what I have so far but it doesn't seem to do anything, can anyone help me understand what I am missing please as I have been looking around and cannot find anything that has helped me with the issue?
Thank you!!
Try this. Google Apps scripts is basically Javascript. This is how I would loop through the columns since the used area of the sheet is dynamically changed, then it will stop at the last used column.
function Hide() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
// get the entire 6th Row
var range = sheet.getRange("6:6");
// get number of columns to loop through
var num_cols = range.getNumColumns();
// loop through columns and check value one by one
// if value is equal to "S", then hide the column
for (var i = 1; i <= num_cols; i++) {
var value = sheet.getRange(6,i).getValue();
if (value == "S") {
sheet.hideColumns(i);
};
}
}

Implement vlookup between two ranges in Google Apps Script

I'm looking to find a way to write a script that behaves similarly to a vLookup in google sheets.
Here is a link to a simplified example document.
What I am trying to do is use "Sheet1" as a form of sorts. When I enter information in "Sheet1" column B I want to be able to hit a custom menu button and have the information in "Sheet1" column B automatically populate correspondingly based of the values in column A into the first empty column on "Sheet 2".
I can write the script to create the custom menu and execute the function but I'm unsure of how to write the function itself.
Here is how such a function can look like. It gets pointers to each sheet, then the appropriate range from each: columns A and B, ignoring empty rows at the bottom. Then gets the values and begins comparing them: when columns A match, column B is assigned to. The final line puts the modified array values back into Sheet2.
function vl() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet1 = ss.getSheetByName('Sheet1');
var sheet2 = ss.getSheetByName('Sheet2');
var range1 = sheet1.getRange(1, 1, sheet1.getLastRow(), 2);
var range2 = sheet2.getRange(1, 1, sheet2.getLastRow(), 2);
var values1 = range1.getValues();
var values2 = range2.getValues();
for (var i = 0; i < values1.length; i++) {
for (var j = 0; j < values2.length; j++) {
if (values1[i][0] === values2[i][0]) {
values2[i][1] = values1[i][0];
}
}
}
range2.setValues(values2);
}

Fill column based on row data

I'm looking to use a button to run a script within a Google Sheet. When the button is pressed, I'd like a column to populate with "Approved" for a given row, based on the user that's logged in and the email that's listed in the row.
For example, Column A has a list of different email addresses. Press a button, and column B is filled with "Approved" if your login email address is the same as what's in column A. Ideally, it could search and make sure the entry in column B isn't already populated with "Rejected". Think about a supervisor going through a list, and instead of using a pull-down for "Approved" on each line, they can hit a button and it would approve all of the entries their email is attached to.
I have it working, but it's slow and inefficient as it goes through each row, evaluates, then populates the appropriate column. It also doesn't set the approval for the last row of data. It runs through each row, populates, but doesn't fill the Approved on the last row of data entered.
I'd like to figure out how to do this with an array to make it more efficient.
Here is what I have so far:
function supervisorApproval(){
//This function will approve all of the peole in the respected supervisors section
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("AS");
var approverLogin = Session.getActiveUser().getEmail();
var ui = SpreadsheetApp.getUi();
//Calculate which row is the last row (last entry)
var column = sheet.getRange('W:W');
var values = column.getValues();
var lastrow = 1;
//this is inefficient code, doesn't write "approved" for last row of data
while (values[lastrow][0] != ""){
var supEmail = sheet.getRange(lastrow, 23).getValue();
var approvalCell = sheet.getRange(lastrow, 27);
if (approverLogin == supEmail){
approvalCell.setValue("Approved");
}
lastrow++;
}
}
It should be a lot faster if you read and write values to the spreadsheet only once. Try this.
function supervisorApproval() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("AS");
var approverLogin = Session.getActiveUser().getEmail();
var ui = SpreadsheetApp.getUi();
var range = sheet.getRange(1, 23, sheet.getLastRow(), 5);
var values = range.getValues();
for(var i = 0; i < values.length; i++) {
var row = values[i];
var supEmail = row[0];
if (approverLogin === supEmail){
row[4] = "Approved";
}
}
range.setValues(values);
}

Google Apps Script - Appending data from spreadsheet to table in document template

I am trying to write a script that will take a list of expenses from a google sheet and append them to a already existing table in a google docs template. At the moment I have this (the reason it says names is because I am testing with dummy data of names and ages):
function myFunction() {
var ss = SpreadsheetApp.getActive();
var sheet = ss.getActiveSheet()
var numRows = sheet.getLastRow()
var numColumns = sheet.getLastColumn()
var data = sheet.getRange(1, 1, numRows, numColumns).getValues()
var doc = DocumentApp.openById('13l6O8nmEZgJiZnzumWihsRVOZiq_8vUj6PtBtb9My_0')
var body = doc.getBody()
var tables = body.getTables()
var table = tables[1]
for (var i = 1; i < data.length; i++){
var row = data[i]
var name = row[0]
var age = row[1]
var state = row[2]
var done = 'Done'
//Check to see if line has already been processed or not
if (!state){
sheet.getRange(i + 1, 3).setValue(done)
//Slices out the blank state value
var slice = row.slice(0, numColumns-1)
table.appendTableRow(slice)
}
}
}
This just adds a new table to the document but I can't find way to add rows to an existing table of the data I can add indvidual cells one per row but that isn't useful and can't seem to/don't understand how the appendtoRow instruction works. Any thoughts on how best to do this?
To add a row to an existing table you need to use appendTableRow() and then add cells to this newly added row.
Change the last part of your script like below (make sure that your table is indeed the second table in the document since you used var table = tables[1])
...
if (!state){
sheet.getRange(i + 1, 3).setValue(done);
//Slices out the blank state value
var slice = row.slice(0, numColumns-1);
Logger.log(slice);
var tableRow = table.appendTableRow();
for(var n in slice){
tableRow.appendTableCell(slice[n]);
}
}
...

Change copyDown() Script to operate on just one Sheet

In Google Sheets I have a sheet with Form Responses, and to the right of the form columns I have columns with formulas that use form data for functions.
At the start I had the formulas extended down the rows so they worked on new form submissions, but found that new form submissions would clear the row out :(.
Instead of manually extending formulas down after each submission I installed Andrew Stillman's copyDown() script; what it does is copies down the formulas after scripts are submitted.
Now the problem I'm having is that the script works when run manually, but when I set to trigger on form submits it copies the said formula on that sheet and on all other sheets in the spreadsheet. I Do Not Want that side-effect, as it messes the whole spreadsheet up. :((
What I thought to do is edit script so it only works on the one Form Response sheet, not all sheets. But I don't know how to do that.
The name of the sheet I want it to run on is "Requests", and the gid=8.
How do I edit this script to only work for that one sheet?
To get code to run on just a particular sheet use the .getSheetByName() method. For example:
var ss = SpreadsheetApp.getActiveSpreadsheet();
var reqSh = ss.getSheetByName('Requests');
There is another way that might be easier. You could try keeping one sheet purely for form submissions and use an arrayformula in a second sheet to copy any values from the first sheet to the same range in the second.
=ARRAYFORMULA('Requests'!A1:H) would copy columns A to H.
I had the same problem as you and this was the solution. I placed my formulas in the second sheet in the columns to the right of the range and copied them down in the normal way. The formulas referred to the copied range in the second sheet. It worked a treat.
I didn't come up with the idea myself - I'm sure it was suggested by someone on the Google spreadsheet forum. I should give you a link to the post but I just looked and I can't find it.
In your code you have (comments in code)
var sheets = ss.getSheets() [8]; // you choose sheet [8]
var cellAddresses = new Object();
for (var i=0; i<sheets.length; i++) { // but you enter a for loop that adresses every sheet in turn...
var range = sheets[i].getDataRange();
You should simply suppress this loop and use only the sheet number you want to be proceeded ...
The simplest way could be to do it like this :
var i = 8
var sheets = ss.getSheets() [i];
var cellAddresses = new Object();
var range = sheets[i].getDataRange();
...
and at the end of the loop delete the } that fitted the for loop
EDIT : the new code should be like this :
function copydown() {
setCopyDownUid();
setCopyDownSid();
logCopyDown();
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheets = ss.getSheets() [8];
var cellAddresses = new Object();
var i=8
// for (var i=0; i<sheets.length; i++) {
var range = sheets[i].getDataRange();
var lastRow = range.getLastRow();
var values = range.getValues();
for (var j=0; j<values.length; j++) {
for (var k=0; k<values[j].length; k++) {
var test = values[j][k].toString();
var start = test.indexOf("copydown");
if (start == 0) {
start = start+10;
var end = test.length-2;
var length = end-start;
var value = test.substr(start, length);
var col = k+1;
var nextRow = j+2;
var numRows = lastRow-(nextRow-1);
if (numRows>0) {
var destRange = sheets[i].getRange(nextRow, col, numRows, 1);
destRange.clear();
var newLastRow = sheets[i].getDataRange().getLastRow();
var newNumRows = newLastRow-(nextRow-1);
var newDestRange = sheets[i].getRange(nextRow, col, newNumRows, 1);
var cell = sheets[i].getRange(nextRow-1, col);
cell.setFormula(value);
cell.copyTo(newDestRange);
}
var cellAddress = cell.getA1Notation();
cellAddresses[cellAddress] = test;
}
}
}
Utilities.sleep(500);
resetCellValues(cellAddresses, sheets[i]);
}
//}