I'm pulling my hair out a little trying to write a conditional Google apps script for Google sheets. The sheet has 5 tabs. Basically, the sheet gets it's data from a google form. Clients fill the form out and it populates our "Master" tab of the sheet in question.
Here's where I'm hung up:
The form is used for teachers to refer students to specific administrators. Whoever is controlling the form needs to be able to assign a specific row of data to a specific administrator, which each have their own tab set up. They do this by selecting, from a drop down, which admin they want to assign the row to.
So, if row 2 is assigned to "Miranda," Certain data would get moved over to the "Miranda" tab. For the sake of argument lets say column D and E of that particular row would get moved over to column A of the "Miranda" tab.
Here's what I've come up with so far:
function importStudent() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var master_sheet = ss.getSheetByName("Master");
var miranda_sheet = ss.getSheetByName("Miranda");
var columnT = master_sheet.getRange("T1:T").getValues();
var columnD = master_sheet.getRange("D1:D").getValues();
var columnE = master_sheet.getRange("E1:E").getValues();
for(var i = 0; i <= columnT; i++) {
if (columnT[i][0] === "Miranda") {
var miranda_row = (miranda_sheet.getLastRow() + 1);
var active_row = SpreadsheetApp.setActiveRange(columnT[i].getRow());
var selectionFirst = columnD.setActiveRange(active_row:columnD);
var selectionLast = columnE.setActiveRange(active_row:columnE);
var fullName = selectionFirst + " " + selectionLast;
fullName.copyValuesToRange(miranda_sheet, 1, 1, miranda_row, miranda_row);
}
}
}
The below code is broken, and i'm looking for a little guidance in making it work. Right off, I know I have at least two issues: 1 there are too many variables, and 2 I'm not sure I can pass a variable as a parameter for the setActiveRange() class. So, my questions:
How can I clean this up? And what (class?) should I be using in order to make it functional?
PS - I still consider myself a novice when it comes to JavaScript. I have the knowledge, but the practical application is something I'm still learning :)
not sure if you need to use script to do what you are trying. Why not use a query formula?
For example:
=query(MasterData!:A:E,"SELECT D,E WHERE A = 'Miranda'",0)
There is some help on queries here. You can also query across spreadsheets. That is to say you could assign Miranda her own spreadsheet, which feeds from your master data.
Please share with me a sample spreadsheet data if you need more assistance.
Related
I am at starting web dev, already using html/css.
For a little project, I had a look at JavaScript. (My goal is that when people click a button, the site will show a random sentence that will be taken from a google sheet cell.)
Could you tell me please if it is even possible? If so, please share some ideas that I will explore. If not, please give me some alternative ideas... Thanks so much.
Have a good day!
-LeganV9
This is possible using Google Apps Script!
I have a working demo here, with the source being here. I dare you to get the jackpot. :D
In order to make this, you can go to https://script.new. Now, in code.gs put this:
function doGet() {
return HtmlService.createTemplateFromFile("index").evaluate().setXFrameOptionsMode(HtmlService.XFrameOptionsMode.ALLOWALL);
}
function getVals(){
var ss = SpreadsheetApp.openByUrl("https://docs.google.com/spreadsheets/d/1IDbhQhaImcQB-4j-iByajwAkvxkutptcPMhMTxNrPtU/edit#gid=0");//Put your URL here
var sheet = ss.getSheetByName("Sheet1");//Put your sheet name here
var AMOUNT_OF_SENTENCES = sheet.getMaxRows().toString().replace(".0","");//You can replace this with a number eg 20
var range = sheet.getRange(1, 1,AMOUNT_OF_SENTENCES);
var values = range.getValues();
var newValues = [];
for(var i = 1; i<values.length;i++){
if(values[i][0] === "" || values[i][0] === " "){
}else{
newValues.push(values[i][0]);
}
}
return {valuesVar: newValues };
}
After that, create a new HTML file called "index" and put this in it:
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<h1>
The results are: <span id = "results">Loading...</span>
</h1>
<button id = "yourIdHere">Click me!!</button>
<script>
var yourDataList;
function onSuccess(data) {
yourDataList= data.valuesVar;
}
google.script.run.withSuccessHandler(onSuccess).getVals();
var myBtn = document.querySelector("#yourIdHere"); //Declare button, replace yourIdHere with your ID
var randomNum = 0; //Decclre random number
function getRandom() { //Declare your function
randomNum = parseInt(Math.random() * yourDataList.length); //Get random number between 0 and the length of your datalist
return yourDataList[randomNum]; //Return your value
}
myBtn.addEventListener("click", function() { //Bind a listener to the button
document.querySelector("#results").innerText = getRandom(); //Do whatever you want to with the random value
});
document.querySelector("#results").innerText = getRandom();//Change from loading...
</script>
</body>
</html>
Welcome to the world of web development! Hope your project is a success.
It should definitely be possible, since Google Sheets offers an API which has read/write functionality (https://developers.google.com/sheets/api).
You could even later extend this so people can submit their own sentences, given that writing to a Google Sheet is also possible with this API.
However, since you're starting out, consider treating this as an iterative process. You don't have to publish your first version, but just to prevent overwhelming yourself, you might want to set small milestones along the way - each adding more functionality. For example:
Create an array of random sentences (you could, for example, start with using this to keep it simple: https://github.com/JamesFT/Database-Quotes-JSON).
Select and log a random sentence to the console (console.log()) each time the script is executed.
Transfer the random sentence to render in HTML and allow a new sentence to be generated each time a button is pressed.
Move your sentences into a Google Sheet and begin exploring the API.
This way, you achieve something in a much shorter space of time, while working towards your end goal. It's a good way to keep motivated and make things more manageable.
Best of luck!
Having a lot of trouble finding this and as a very beginner programmer, I can't quite troubleshoot my way through this.
What I want to do:
Automatically log the word count of a google doc in a google sheets cell.
The code I've been playing with to try and make it happen that is probably super wrong:
function countWords() {
var doc = DocumentApp.openByURL().getBody().getText();
var punctuationless = doc.replace(/[.,\/#!$%\^&\*;:{}=\-_`~()"?“”]/g," ");
var finalString = punctuationless.replace(/\s{2,}/g," ");
var count = finalString.trim().split(/\s+/).length;
return count;
Ideally, what I'd like to do is, in sheets, set it up so there's a column with links to google docs and be able to just put in a function that will return the wordcount from that doc.
Answer:
You can not create a custom function to do this, as reading another document requires authentication. You can however do this with an in-sheet button which runs the script.
More Information:
As per the documentation on custom functions, it is not possible to run methods which require authentication such as DocumentApp:
Unlike most other types of Apps Scripts, custom functions never ask users to authorize access to personal data. Consequently, they can only call services that do not have access to personal data
As a result, you will instead have to manually run the script - but this can be done from a button in the Sheet.
Code:
Assuming that you have the Document links in column A and wish for the word count to be in column B (starting in row 2):
function countWords() {
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1");
var linkRange = ss.getRange("A2:A");
try {
linkRange.getValues().forEach(function(cell, index) {
if (cell[0] == "") {
throw "Cell A" + (index + 2) + " is empty"
}
let doc = DocumentApp.openByUrl(cell[0]).getBody().getText();
let count = (doc.match(/\b\S+\b/g) || []).length;
ss.getRange(index + 2, 2).setValue(count);
});
}
catch (err) {
console.log(err);
return;
}
}
Rundown of this function:
Open the sheet containing the document links (remember to change the sheet name!)
Get the range of links down column A
Loop through each link and obtain the Document's text
Obtain all instances of word-boundary/non-whitespace/word-boundary in the document, puts them all into an array, and gets the length of the array.
In this step, if the document is empty, then an empty array is given
Sets the cell in column B adjacent to the link to the result of the count.
This is all wrapped inside a try/catch so that the script stops execution when it reaches an empty cell in column A.
Assigning to a Button:
Now, you can create an in-sheet button which will run the script whenever you click it.
Go to the Insert > Drawing menu item and create a shape; any shape will do, this will act as your button.
Press Save and Close to add this to your sheet.
Move the newly-added drawing to where you would like. In the top-right of the drawing, you will see the vertical ellipsis menu (⋮). Click this, and then click Assign script.
In the new window, type countWords and press OK.
Now, each time you click the button, the script will run.
Visual Example:
References:
Custom Functions in Google Sheets | Apps Script | Google Developers
I need your help.
I have an active google form where customers register.
When I submit the form I have 3 activators that run me scripts, especially they convert the ITALIAN date format (dd-mm-yyyy) to USA (yyyy-mm-dd).
Start of action: Upon submitting the form
The problem I find is this.
These scripts do not always work even if from the control panel I find that it has been executed correctly without reporting errors.
A code example:
function respondToFormSubmit() {
var ss = SpreadsheetApp.openById("xxxxxxxxxxxxxxxxxxxxxxxx");
var sheet = ss.getSheets()[0];
// Format column I
var column1 = sheet.getRange("F:F");
var column2 = sheet.getRange("J:J");
var column3 = sheet.getRange("K:K");
var column4 = sheet.getRange("A:A");
// Set new date format on column I
column1.setNumberFormat('yyyy-mm-dd');
column2.setNumberFormat('yyyy-mm-dd');
column3.setNumberFormat('yyyy-mm-dd');
column4.setNumberFormat('yyyy-mm-dd');
};
When the problem occurs, all 3 triggers fail.
The non-functioning occurs 20/30% of the time and this discontinuous "error" does not make me understand what the problem is.
Do you have any suggestions for me?
Thank you so much for your invaluable help.
Mauro
The problem with your script running on trigger are likely propagation
issues
It takes some time for a new form response to be inserted into the
spreadsheet, so your number formatting functionality might be run
before the new row gets inserted.
One thing you can do is implement some waiting time at the beginning
at the function, e.g. with
sleep().
However, if you bind your script to the destination spreadsheet
instead of the form itself - you will be able to use the Google
Sheets event
objects
for Form submit which include range.
Sample usage:
function respondToFormSubmit(e) {
var row = e.range.getRow();
var sheet = e.range.getSheet();
sheet.getRange("F" + row).setNumberFormat('yyyy-mm-dd');
sheet.getRange("J" + row).setNumberFormat('yyyy-mm-dd');
sheet.getRange("K" + row).setNumberFormat('yyyy-mm-dd');
sheet.getRange("A" + row).setNumberFormat('yyyy-mm-dd');
};
I am working on a project where I take multiple column/row inventory sheets and turn them into a multi-row/2-column format for order picking.
I have a switch for selecting the appropriate inventory sheet and a map() function that copies the imported information from the inventory DataRange().
However, not all the data is in consistent columns. What I would like to do is find an expression that maps the next column in if the column it was mapping has a zero or "" value.
I won't give you the full body of code unless you need it, but hopefully just the important parts.
This is what I have:
var source = SpreadsheetApp.openById("1xixIOWw2yGd1aX_2HeguZnt8G_UfiFOfG-W6Fk8OSTs"); //This sheet
var srcSht = SpreadsheetApp.getActive();
var sourceMenu = srcSht.getRange('A1');//This is the cell cotaining the dropdown
var menuTest = sourceMenu.getValue();
// Variable for the vars sheet. If it doesn't exist, create it
var varsTest = source.getSheetByName('vars');
if (!varsTest){source.insertSheet('vars');}
var importedA1 = varsTest.getDataRange().getA1Notation();
varsTest.clearContents();
var t1Imp = '=ImportRange("test1_Id", "Stock!A1:F11")';
var varsData = varsTest.getRange('A1');// This is the cell we fill with the importRange formula
varsData.setValue(t1Imp);
var imported = varsTest.getDataRange().getValues();
var newOrder = imported.map(function(item) {
if (item[4] !== NaN){return [[item[0]],[item[4]]];};
if (item[4] === NaN){return [[item[0]],[item[3]]];};}
var orderRange = source.getSheetByName('Sheet1').getRange(10,1,newOrder.length, newOrder[0].length);
orderRange.setValues(newOrder);
Logger.log("\t" + newOrder);
Logger.log(newOrder):
[(timestamp omitted)] items1,order,caramel,6,c&c,2,mint,3,PB,0,,,items2,,caramel,,strawberry,,mint,,PB,
It seems to be skipping the if statements, or I told it that I mean to test the index as NaN, which will obviously never be true.
I also tried replacing 'NaN' with 'undefined'. Same result. I tried finding the item[4].Values, but it gave me an error. I also tried the same logic using filter() instead of map() but it copied the entire data set.
I pull these values onto a new 'vars' sheet in the workbook (to minimize calls to the web service):
test1
reduce them to the first and last columns, then output:
test
The cells in the 'order' column for the second set of items in the 'test' sheet are blank. The values for that order column should be in item[3] of that array, but I can't get the script to identify that that the blank cells are blank.
I am new to Google Apps Script and JS, but I am watching a lot of tuts and learning by doing. If I find a solution, I will post it.
Thank you StackOverflow, I could not have learned as much as I have without this community!
I have a working function that does what I want. In short:
I had to create a duplicate of the order column in a new column, so that all the values would line up. It's not technically a JS answer, but was the simplest and follows good spreadsheet rules.
function rmZeroOrderPS(item){
var source = SpreadsheetApp.openById("<sheetId>"); //This sheet
var varsTest = source.getSheetByName('vars');
var imported = varsTest.getDataRange().getValues();
var i=-1;
while (i <= imported.length){
if(item[8]!= 0) {return [item[0],item[8]]};
i+=1;
};
After around two to three hours of digging across many sites, I cobbled together this functioning script to watch an "input" column on a sheet and, using onEdit(), whenever data is put into that column, move the data further down the sheet to the next available cell in that specific row.
function onEdit(e) {
var s = e.source.getActiveSheet();
var sheetName = 'Pricing Chart';
var colToWatch = 5;
var copyFrom = 5;
var nextEmptyCol = colToWatch + 22;
var emptyCellCheck = s.getRange(e.range.rowStart, nextEmptyCol,1,1);
if (s.getName() !== sheetName || e.range.columnStart !== colToWatch) return;
while (emptyCellCheck.isBlank() !== true){
nextEmptyCol++;
var emptyCellCheck = s.getRange(e.range.rowStart, nextEmptyCol,1,1);
}
if (emptyCellCheck.isBlank()){
s.getRange(e.range.rowStart, copyFrom,1,1)
.copyTo(s.getRange(e.range.rowStart, nextEmptyCol,1,1), {contentsOnly: true}),
s.getRange(e.range.rowStart, copyFrom,1,1).clear({contentsOnly: true});
}
}
Specific example:
Into cell E5, type "222". If AA5 is empty, it will copy "222" into AA5, and then clear E5. If AA5 is not empty, it will check AB5, then AC5, etc until it finds an empty cell. On my specific sheet, it then uses all the data from the row and displays various calculations (average, max, etc) so that those data are visible, but all the individual inputs are tucked away behind the scenes. It's been useful for keeping a large list of data on many different variables in a format that's easy to look at and easy to share with others.
Problem is... sometimes it will do as intended at first, find the next available cell, copy the data, and erase the original input cell (e.g. E5). But, sometimes, it will also erase the cell the data was copied TO (e.g. AB5). Roughly once or twice every ten iterations of the script.
So, I was wondering if anyone could have a look at my script and give me tips on optimizing it or just doing things better so the script runs correctly consistently.