I'm making a Google Sheet for a TTRPG I play with some friends and I'm trying to find the best way to make automated cell notes. I found a solution here that works and modified it to match my sheet but I'm VERY new to Google Scripts and am sure I'm doing things inefficiently.
Right now the solution is:
function onEdit(e){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Copy of Digital');
var range = sheet.getRange("R31:T59");
range.clearNote();
for (var x=1;x<30;x++) {
range.getCell(x, 1).setNote(range.getCell(x, 3).getValue());
}
}
This is a workable solution for my needs but I always want to take things farther than just "workable" :). I know a small amount of C# but JavaScript is entirely new to me. The issues I see with my current implementation and possible areas of improvement are:
Because of formatting requirements on another section of the sheet, every other row where I am running this solution will ALWAYS be empty, but the solution is going to check the empty rows for notes every time. Is there a way for me to have the solution skip every second row? Can I specify exact cells rather than a range (and does JavaScript use absolute references like sheet formulas)?
Can I specify multiple ranges within the sheet for this solution to act on? Is it as simple as defining a second range?
Is it possible to restrict the onEdit(e) function's activation to only fire when specific ranges are edited? I'm generating a list of "Talents" that need the script to run whenever a player changes their gear through a validation list but to my understanding (and testing) onEdit(e) is going to run when any cell is modified (which will lead to numerous unneccessary script activations).
If I have multiple copies of the same tab active in the same sheet is it possible for multiple players to be using their own version of the sheet at the same time? (I think this must require the removal/modification of the getSheetByName line, but I'm only 2 days into trying to learn this!)
Huge apology for having so many questions! Any and all help is deeply appreciated!
Visual of the sheet for formatting reference
Try it this way:
function onEdit(e) {
const sh = e.range.getSheet();
if (sh.getName() == "Copy of Digital") {
var range = sheet.getRange("R31:T59");
let notes = sheet.getRange("T31:T59").getDisplayValues()
range.clearNote();
sh.getRange(31,18,notes.length,notes[0].length).setNotes(notes);
}
}
Related
I've been trying to learn on my own by watching YouTube videos and reading forums, but I'm still struggling and...I'm a little embarrassed because it seems so simple. I'm hoping any answers/suggestion from you will help me in the future.
I have two sheets "Bishop" and "Log". I want to copy two specific cells (K11 and K12) from "Bishop", which are vertically aligned, and paste them horizontally in the sheet "Log" at C4 and D4, respectively, without any formatting or formulas being copied over.
Each time I press a button (I've already made and attached a script to) I want it to paste in those two cells, and then move down to the next empty row (so C4,D4 to C5,D5, C6,D6...and so on).
I never intended on getting someone to finish my script for me, but I'm getting confused by the examples I've found in my research. Here's my script so far (after deleting many dysfunctional versions)
function Logger(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var bishop = ss.getSheetByName('Bishop');
var log = ss.getSheetByName('Log');
}
I tried to copy and modify code examples from YouTube videos and articles, but I think my by modifying them I'm just making a mess.
Thank you for any help or suggestions.
Dustin
EDIT
After some tinkering and reading some more, I now have a pretty nifty script that copies data in to where I want it! However, my next goal is to learn how to move down one row each time I run the script, so it creates an actual list, or log. "InputData" and "PasteRange" are named ranges on my source sheet and destination sheet, respectively. Here's my code so far:
function Logger(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var bishop = ss.getSheetByName('Bishop');
var log = ss.getSheetByName('Log');
ss.getRange("InputData").copyTo(log.getRange("PasteRange"),SpreadsheetApp.CopyPasteType.PASTE_VALUES,true);
}
So far your script has gotten to the point of creating 2 variables that reference the Bishop and Log sheets.
Next, you'll need to select the specific ranges in those sheets. To get a range in a sheet, use the getRange method, which has a few variations on what inputs it takes. For example:
var range1 = sheet.getRange("A1");
Once you have references to the ranges, you can use the getValue or getValues method to extract the values inside a range, and then use the setValue or setValues method to overwrite new data into an existing range. For example:
var myValues = range1.getValues();
range2.setValues(myValues);
If you haven't seen this page before, take a look; it's probably the most helpful single webpage for writing sheet scripts.
(Also, I won't write the code for you; Stackoverflow is about specific questions and answers, not about figuratively doing your homework, though the lines are blurry at times. But feel free to keep asking questions!)
I made a Google Sheet to check every media that plays on a certain channel on TV using a lot of workaround formulas within the cells themselves. A part of this sheet is a column (G) that tells me whether or not the specific episode/media/whatever is currently playing, has played in the past or will be played later today/at a later date using a "NOW" function. Next to that column there is another (F) where the user is able to write a "V", and in the case the show is playing but the user hasn't checked it yet, it writes "Check Me" See Example.
I wanted to create a button that will automatically change that "Check Me" into a "V" but the problem is that "Check Me" is based on a simple formula written throughout column F (=IF(G5="Playing","Check Me","")), so when I tried to run a script I found here on StackOverflow:
function Test() {
var sheet = SpreadsheetApp.getActiveSheet();
var range = sheet.getRange("F5:F700");
range.setValues(range.getValues().map(function(row) {
return [row[0].replace(/Check Me/, "V")];
}));
}
(Can't remember the exact thread I got it from and it's been two days since I found it with lots of similar searches in my history, so I apologize for not crediting)
together with its intended use, it also straight up deleted all the rest of the formulas from the column, probably due to the formula itself containing "Check Me" but I might be mistaken.
To be honest, before this week I barely ever worked with either Google Sheets, much less JavaScript or even coding in general, so I'm pretty much restrained to changing values and very minor modifications in scripts I find online.
The only idea I had as to how to solve it is to add an "if IsBlank" but regarding face value of the cell only rather than its contents, but I don't know how to do it or whether it is even possible in the first place. At the very least, google shows no results on the subject. Is there a way to add that function? or perhaps a different method altogether to make that button work? (it's a drawing I will assign a script to)
Because you're using a map function to update the range, you'll need to get the formulas using getFormulas() in addition to the display values using either getValues() or getDisplayValues(). Using only the display values, as you're currently doing, will cause you to lose the formulas when you update the sheet. Conversely, using only the formulas would cause you to lose all of the display values that don't have a formula, so you'll need both. Try this and see if does what you want:
function Test() {
var sheet = SpreadsheetApp.getActiveSheet();
var range = sheet.getRange("F5:F700");
// Get all cell formulas and display values
var formulas = range.getFormulas();
var values = range.getValues();
range.setValues(formulas.map(function(row, index) {
var formula = row[0];
var value = values[index][0];
// Check only the display value
if (value == "Check Me") {
return ["V"];
} else {
// Return formula if it exists, else return value
return [formula || value];
}
}));
}
For some reason the formula isn't working when the data from importrange is being updated. What I have to do is to go to cells and pressing F2+enter to refresh and ya it works like that I tried creating a macro and it also works but I want it to be simplified.
I come up with this kind of script that doesn't really work because i don't have time to learn for and dig deeper for scripting
function CellRefesher() {
var spreadsheet = SpreadsheetApp.getActive();
var range = spreadsheet.getRange("A1:O52");
range = SpreadsheetApp.flush();
};
so What I'm trying to do is I want to do something like force calculation for the cell range of A1 to O52 and base of google document(just base on my understanding)
this "SpreadsheetApp.flush();" will do something similar that's why I do this "range = SpreadsheetApp.flush();"
This might be like a spoon feeding but please do help me. I want to learn but don't just have much time yet. Thank you
I am working on a tool that will allow my company to track its financial investments.
I need to create a simple data entry form for users to input transaction data, which will then populate a master sheet which is the basis for the analyses the tool does. I cannot do this via Google Forms because the data entry form uses a lot of conditional formatting based on other data in the sheet.
I have uploaded a very simplified sheet to illustrate what I need: this
What I am looking for is a script that, upon clicking Submit in the Data Entry sheet, copies the values (NOT the formulas) in B12:E12 to the first empty row (in this case, row 8) in the "Master Table" sheet. Ideally, clicking "Submit" will also clear the data entry fields in C4:C7 in the "Data Entry" sheet.
I have looked through various forums for a solution but have not found anything that does exactly that. I am sorry to say I am a complete newbie at Google Apps Script, therefore I could not write my own code to share, which I am aware is customary when asking a question here.
If anyone could point me in the right direction regardless, it would be much appreciated. I am currently trying to learn JavaScript and Google Apps Script using online resources, but for this specific project, it would take too long for me to reach a level where I could help myself.
Thank you very much, GEOWill!
Your answer solved my problem and thanks to your comments, I was able to understand exactly what your code does. I changed the code only to remove the Menu (but thank you very much for showing me how that is done anyway) - I tied the function to a Submit button inserted as a drawing. I also added some code to clear the contents of the entry range after clicking the button (this was suggested by someone else).
The final code that I used is:
var ss = SpreadsheetApp.getActiveSpreadsheet();
var entry_sheet = ss.getSheetByName("Data Entry");
var master_sheet = ss.getSheetByName("Master Table");
function mySubmit() {
var entry_range = entry_sheet.getRange("B12:E12")
var val = entry_sheet.getRange("B12:E12").getDisplayValues().reduce(function (a, b) {
return a.concat(b);
});
Logger.log(val);
master_sheet.appendRow(val);
entry_sheet.getRange("C4:C7").clearContent()
}
I hope this helps others with a similar problem! Love how supportive this community is. Thanks for helping out.
I would begin by using a menu function to run your code in (rather than a cell button). You could use a cell button, but I believe you need to insert an image and assign a javascript function to that image anyways.
Basically, you begin by going to tools, script editor. Create an onOpen function and create a trigger that runs the onOpen() function each time the spreadsheet is opened. Inside the onOpen() function, we create a menu into which a physical menu item (a kind of button) exists (called 'Submit'). Finally, we associate a function to this button (I called it mySubmit()).
Inside of the mySubmit() function is where most of the functionality you are looking for exists. At this point, it is just a matter of copying from one range of cells and pasting them to another range of cells. For this, you'll notice that I had to setup a few variables ahead of time (ss, entry_sheet, master_sheet, entry_range, and master_row).
One last thing, you may want to protect the Master table sheet because if someone accidentally edits a cell beyond the last one edited, the input row would be copied to that row (due to how the getLastRow() function operates).
Hope this helps!
var ss = SpreadsheetApp.getActiveSpreadsheet();
var entry_sheet = ss.getSheetByName("Data Entry");
var master_sheet = ss.getSheetByName("Master Table");
function onOpen() {
SpreadsheetApp.getUi()
.createMenu('MyMenu')
.addItem('Submit', 'mySubmit')
.addToUi();
}
function mySubmit() {
var entry_range = entry_sheet.getRange("B12:E12");
var master_row = (master_sheet.getLastRow() + 1);
entry_range.copyValuesToRange(master_sheet, 1, 3, master_row, master_row);
}
I have searched and searched but can't find anything that works. I am trying to eliminate any empty columns that remain in the spreadsheet when creating a Google Form. It would be helpful because I have a formula for the sum of a string comparison in the sheet, but I don't want it to include comparisons of empty cells. (i.e. I compare the entries in from H:X in every individual row to H2:X2 and have it count how many are the same.) I know I could adjust the formula, but I am looking to build a template for fellow colleagues so they don't have to worry about altering anything. If I could get the empty columns at the end to disappear automatically, I could just change the sum formula to extend all the way to column CZ (just to be sure it goes far enough), without it calculating blanks in its comparison.
Any help would be great! Thanks!
i use these scripts in the script editor of my google sheets for cleaning them up. stumbled across them somewhere... perhaps they will help.
//Remove All Empty Columns in the Entire Workbook
function removeEmptyColumns() {
var ss = SpreadsheetApp.getActive();
var allsheets = ss.getSheets();
for (var s in allsheets){
var sheet=allsheets[s]
var maxColumns = sheet.getMaxColumns();
var lastColumn = sheet.getLastColumn();
if (maxColumns-lastColumn != 0){
sheet.deleteColumns(lastColumn+1, maxColumns-lastColumn);
}
}
}
//Remove All Empty Rows in the Entire Workbook
function removeEmptyRows() {
var ss = SpreadsheetApp.getActive();
var allsheets = ss.getSheets();
for (var s in allsheets){
var sheet=allsheets[s]
var maxRows = sheet.getMaxRows();
var lastRow = sheet.getLastRow();
if (maxRows-lastRow != 0){
sheet.deleteRows(lastRow+1, maxRows-lastRow);
}
}
}
This is something you will have to get used to when using Google forms. Data is never really erased if you physically delete rows/columns, they will just reappear at some unknown time in the future. It's a pretty buggy and unreliable setup.
My advice is to keep your formulas and data completely separate. You do this by copying the form responses to another sheet (using code or otherwise). That sheet allows you to control the data being fed (or NOT fed) to the rest of your system. I would also go 1 step further and use formulas like
ImportRange(SheetSource!A:A)
in a 3rd sheet to allow you to control the order of the columns. From there you can add your formulas to act on the data being fed through in relative safety.
However, I had a situation not long ago, where the form and the sheet randomly lost their linkage, and it was not possible to restore it. I stayed up until 6am trying to fix the issue, however if I did not have the data copied to the separate sheet (as described above) the catastrophe would have been complete.
Luckily this appeared to have been a random glitch, because everything went back to normal at about 10am, but put simply, your data is not truly safe with Google Forms.
Using Arrayformula is better suited for this though it does the same thing.
Example:
=ARRAYFORMULA('Form Responses'!A1:EA)
Importrange has a noticeable (and annoying) delay as this what you would use if you wanted to reference sheets from other spreadsheet files. Arrayformula references sheets within the same file, no delays.