I have a spreadsheet where there are a large number of sheets detailing information for each "job" the person filling out the sheet can have. To clean this up, I wrote a script to hide or show the sheets based on which jobs they chose on the first page of the sheet - they can choose up to 3.
The script... works, but I've gotten errors saying it's trying to do too much at once and failed. I'm not exactly a great programmer so how to clean this up is, as of yet, fairly beyond me. I'm not looking for the most efficient, I'm just looking for something that works easily enough.
I googled the problem a few times, but a lot of the solutions I saw didn't seem to exactly fit what I was doing, and involved things like arrays and dictionaries?
function myFunction() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet1 = ss.getSheetByName("Character Sheet");
var sheet2 = ss.getSheetByName("Marauder Abilities");
var sheet3 = ss.getSheetByName("Warrior Abilities");
var sheet4 = ss.getSheetByName("Dark Knight");
var sheet5 = ss.getSheetByName("Gladiator");
var sheet6 = ss.getSheetByName("Paladin");
var sheet7 = ss.getSheetByName("Conjurer");
var sheet8 = ss.getSheetByName("White Mage");
var sheet9 = ss.getSheetByName("Arcanist");
var sheet10 = ss.getSheetByName("Scholar");
var sheet11 = ss.getSheetByName("Astrologian");
var sheet12 = ss.getSheetByName("Pugilist");
var sheet13 = ss.getSheetByName("Monk");
var sheet14 = ss.getSheetByName("Lancer");
var sheet15 = ss.getSheetByName("Dragoon");
var sheet16 = ss.getSheetByName("Rogue");
var sheet17 = ss.getSheetByName("Ninja");
var sheet18 = ss.getSheetByName("Samurai");
var sheet19 = ss.getSheetByName("Archer");
var sheet20 = ss.getSheetByName("Bard");
var sheet21 = ss.getSheetByName("Machinist");
var sheet22 = ss.getSheetByName("Summoner");
var sheet23 = ss.getSheetByName("Thaumaturge");
var sheet24 = ss.getSheetByName("Black Mage");
var sheet25 = ss.getSheetByName("Red Mage");
var sheet26 = ss.getSheetByName("Garlean Pureblood");
var cell1 = sheet1.getRange('B5');
var cell2 = sheet1.getRange('C5');
var cell3 = sheet1.getRange('D5');
if (cell1.getValue() != "Marauder"||cell2.getValue() != "Marauder"||cell3.getValue() != "Marauder") {
sheet2.hideSheet();
}
if (cell1.getValue() == "Marauder"||cell2.getValue() == "Marauder"||cell3.getValue() == "Marauder") {
sheet2.showSheet();
}
if (cell1.getValue() != "Warrior"||cell2.getValue() != "Warrior"||cell3.getValue() != "Warrior") {
sheet3.hideSheet();
}
if (cell1.getValue() == "Warrior"||cell2.getValue() == "Warrior"||cell3.getValue() == "Warrior") {
sheet3.showSheet();
}
if (cell1.getValue() != "Dark Knight"||cell2.getValue() != "Dark Knight"||cell3.getValue() != "Dark Knight") {
sheet4.hideSheet();
}
if (cell1.getValue() == "Dark Knight"||cell2.getValue() == "Dark Knight"||cell3.getValue() == "Dark Knight") {
sheet4.showSheet();
}
It goes on from there for all 26 sheets.
Is there an easier way to write this massive thing out?
Could I maybe do
if (cell1.getValue() == "Marauder"||cell2.getValue() == "Marauder"||cell3.getValue() == "Marauder")
{
sheet2.showSheet();
}
else
{
sheet2.hideSheet();
}
I think this does it:
function myFunction() {
var sA=["Marauder Abilities","Warrior Abilities","Dark Knight","Gladiator","Paladin","Conjurer","White Mage","Arcanist","Scholar","Astrologian","Pugilist","Monk","Lancer","Dragoon","Rogue","Ninja","Samurai","Archer","Bard","Machinist","Summoner","Thaumaturge","Black Mage","Red Mage","Garlean Pureblood"];
var cA=["Marauder","Warrior","Dark Knight","Gladiator","Paladin","Conjurer","White Mage","Arcanist","Scholar","Astrologian","Pugilist","Monk","Lancer","Dragoon","Rogue","Ninja","Samurai","Archer","Bard","Machinist","Summoner","Thaumaturge","Black Mage","Red Mage","Garlean Pureblood"];
var ss=SpreadsheetApp.getActive();
var sheet1=ss.getSheetByName("Character Sheet");
var cells=sheet1.getRange('B5:D5').getValues()[0];
var shts=ss.getSheets();
for(var i=0;i<shts.length;i++) {
var index=sA.indexOf(shts[i].getName());
if(index>-1) {
if(cells[0]!=cA[index] || cells[1]!=cA[index] || cells[2]!=cA[index]) {
shts[i].hideSheet();
}
if(cells[0]==cA[index] || cells[1]==cA[index] || cells[2]==cA[index]) {
shts[i].showSheet()
}
}
}
}
Related
Someone please help.
I am trying to create a simple script which records button presses in order.
I have:
function P1Buzzer() {
var sheet = SpreadsheetApp.getActiveSheet();
var row = 6;
var col = 1;
var Name = sheet.getRange(row, col).getValue();
var sheet = SpreadsheetApp.getActiveSheet();
var row = 192;
var col = 1;
var AnswerPosition = sheet.getRange(row, col).getValue();
if (AnswerPosition = 1) {
sheet.getRange(199, 1).setValue(Name);
var AnswerPosition = AnswerPosition + 1
sheet.getRange(192, 1).setValue(AnswerPosition);
}
if (AnswerPosition = 2) {
sheet.getRange(200, 1).setValue(Name);
var AnswerPosition = AnswerPosition + 1
sheet.getRange(192, 1).setValue(AnswerPosition);
}
}
The problem I have is that when I run the script it does both the commands, ignoring the if command.
Any ideas?
First, you are setting the value to 1 by using = instead of ==. Second, if it was 1, you add one to it and then check to see if it is 2. I'll bet it is!
if (AnswerPosition == 1) {
sheet.getRange(199, 1).setValue(Name);
var AnswerPosition = AnswerPosition + 1
sheet.getRange(192, 1).setValue(AnswerPosition);
} else if (AnswerPosition == 2) {
...
I am writing a function that will search a spreadsheet for days an employee has worked hours, create an array with the values for job number, job name, employee name, hours, and date, and then put them onto another spreadsheet. It also only shows hours that were within the past 8 days so that time can be worked out for the week when we have to report hours (I'm scheduling it to run the day time is due). I also have been filtering out days with blank hours, which limits the amount of data that gets copied over.
I am encountering an issue where on one employee the function to skip the blank hours does not work. For other employees I have been able to use "" to indicate an empty cell. I have also tried to use (null) as a value, but that only ignores 6 of 7 days (It still logs days with no hours that are adjacent to cells that calculate hours in the week).
What I can't figure out is why this doesn't work on just one sheet out of the whole Google Sheets document. I have simplified my spreadsheet to reduce personal information, and to make it easier to parse the script, but in my original document I track 6 employees with similar code, and only one is showing this issue.
https://docs.google.com/spreadsheets/d/1ve0EPVQJ2vmWG1NYHMncw1ZljP3yXd28dMeNC38Jiy4/edit?usp=sharing
Is a link to the spreadsheet. Code is below.
function shoptime(){
var ss = SpreadsheetApp.getActive().getId();
var stephensheet = Sheets.Spreadsheets.Values.get(ss, 'Stephen!A2:G');
var tiffanysheet = Sheets.Spreadsheets.Values.get(ss, 'Tiffany!A2:G');
var scripts = SpreadsheetApp.getActive().getSheetByName("Scripts");
var currentDate = new Date();
var pastweek = new Date();
pastweek.setDate(currentDate.getDate() -8);
var array=[];
for (var a = 0; a < stephensheet.values.length; a++){
var jobdate = stephensheet.values[a][1];
var intime = stephensheet.values[a][2];
var outtime = stephensheet.values[a][3];
var dailyhours = stephensheet.values[a][4];
if (new Date(jobdate) > pastweek){
if (dailyhours != (null)){
array.push(["NA","Office","Stephen",dailyhours,jobdate]);
}
}
}
for (var a = 0; a < tiffanysheet.values.length; a++){
var jobdate = tiffanysheet.values[a][1];
var intime = tiffanysheet.values[a][2];
var outtime = tiffanysheet.values[a][3];
var dailyhours = tiffanysheet.values[a][4];
if (new Date(jobdate) > pastweek){
if (dailyhours != ("")){
array.push(["NA","Office","Tiffany",dailyhours,jobdate]);
}
}
}
if(array[0]){
scripts.getRange(scripts.getLastRow()+1,1,array.length,5).setValues(array);
}
SpreadsheetApp.flush();
}
I'm not sure of the exact issue but checking for cells that have a value rather than cells that are not null should work for you.
function shoptime(){
var ss = SpreadsheetApp.getActive().getId();
var stephensheet = Sheets.Spreadsheets.Values.get(ss, 'Stephen!A2:G');
var tiffanysheet = Sheets.Spreadsheets.Values.get(ss, 'Tiffany!A2:G');
var scripts = SpreadsheetApp.getActive().getSheetByName("Scripts");
var currentDate = new Date();
var pastweek = new Date();
pastweek.setDate(currentDate.getDate() -8);
var array=[];
for (var a = 0; a < stephensheet.values.length; a++){
var jobdate = stephensheet.values[a][1];
var intime = stephensheet.values[a][2];
var outtime = stephensheet.values[a][3];
var dailyhours = stephensheet.values[a][4];
if (new Date(jobdate) > pastweek){
if (dailyhours){
array.push(["NA","Office","Stephen",dailyhours,jobdate]);
}
}
}
for (var a = 0; a < tiffanysheet.values.length; a++){
var jobdate = tiffanysheet.values[a][1];
var intime = tiffanysheet.values[a][2];
var outtime = tiffanysheet.values[a][3];
var dailyhours = tiffanysheet.values[a][4];
if (new Date(jobdate) > pastweek){
if (dailyhours){
array.push(["NA","Office","Tiffany",dailyhours,jobdate]);
}
}
}
if(array[0]){
scripts.getRange(scripts.getLastRow()+1,1,array.length,5).setValues(array);
}
SpreadsheetApp.flush();
}
And to modify the code to use the built-in service with is much more straight forward in this case;
function shoptime(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var stephensheet = ss.getSheetByName('Stephen').getDataRange().getValues();
var tiffanysheet = ss.getSheetByName('Tiffany').getDataRange().getValues();
var scripts = ss.getSheetByName("Scripts");
var currentDate = new Date();
var pastweek = new Date();
pastweek.setDate(currentDate.getDate() -8);
var array=[];
for (var a = 0; a < stephensheet.length; a++){
var jobdate = stephensheet[a][1];
var intime = stephensheet[a][2];
var outtime = stephensheet[a][3];
var dailyhours = stephensheet[a][4];
if (new Date(jobdate) > pastweek){
if (dailyhours != ""){
array.push(["NA","Office","Stephen",dailyhours,jobdate]);
}
}
}
for (var a = 0; a < tiffanysheet.length; a++){
var jobdate = tiffanysheet[a][1];
var intime = tiffanysheet[a][2];
var outtime = tiffanysheet[a][3];
var dailyhours = tiffanysheet[a][4];
if (new Date(jobdate) > pastweek){
if (dailyhours != ""){
array.push(["NA","Office","Tiffany",dailyhours,jobdate]);
}
}
}
if(array[0]){
scripts.getRange(scripts.getLastRow()+1,1,array.length,5).setValues(array);
}
SpreadsheetApp.flush();
}
Finally, if you are scraping data from 6 sheets in the exact same format then a another loop and an array of the sheet names will save on repetition of code
function shoptime() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheets = ['Stephen', 'Tiffany'] // add the aditional sheet names here
var scripts = SpreadsheetApp.getActive().getSheetByName("Scripts");
var currentDate = new Date();
var pastweek = new Date();
pastweek.setDate(currentDate.getDate() - 8);
var array = [];
for (var i = 0; i < sheets.length; i++) {
var sheet = ss.getSheetByName(sheets[i]);
var data = sheet.getDataRange().getValues();
for (var a = 0; a < data.length; a++) {
var jobdate = data[a][1];
var intime = data[a][2];
var outtime = data[a][3];
var dailyhours = data[a][4];
if (new Date(jobdate) > pastweek) {
if (dailyhours) {
array.push(["NA", "Office", sheets[i], dailyhours, jobdate]);
}
}
}
}
if (array[0]) {
scripts.getRange(scripts.getLastRow() + 1, 1, array.length, 5).setValues(array);
}
SpreadsheetApp.flush();
}
I have a visualization table that has an event listener on select.
The need: I want the user to be able to delete documents on the google drive without having to leave the webpage
The set up: I added a button so that when clicked, I get a confirm alert box that includes the value. Once I click OK, it runs the scripts from the client-side with an event handler. This works perfectly!
The problem: I can move one document at a time but if I need to move 20+ documents it gets really tedious to click rows one after the other. Is it possible to pass multiple values to the successhandler?
google.visualization.events.addListener(archiveChart.getChart(), 'select', function () {
$("#docArchive").on("click", function() {
var selection = archiveChart.getChart().getSelection();
var dt = archiveChart.getDataTable();
if (selection.length > 0) {
var item = selection[0];
var docurl = dt.getValue(item.row, 2);
var docname = dt.getValue(item.row, 1);
var folder = dt.getValue(item.row, 4);
if(confirm("Are you sure you want to archive " + docname + "?") == true) {
archiveChart.getChart().setSelection([]);
return google.script.run.withSuccessHandler(onSuccessArchive).withFailureHandler(function(err) {
alert(err);
}).archiveDoc(docurl,folder);
} else {
archiveChart.getChart().setSelection([]);
}
}});
})
I feel like I might need to add this:
for (var i = 0; i < selection.length; i++) {
var item = selection[i];
I'm struggling a little with understanding what I might need to change (still learning). Any help or guidance is appreciated!
recommend confirming once, for all documents
then loop the selection to archive each document
google.visualization.events.addListener(archiveChart.getChart(), 'select', function () {
$("#docArchive").on("click", function() {
var selection = archiveChart.getChart().getSelection();
var dt = archiveChart.getDataTable();
var docNames = selection.map(function (item) {
return dt.getValue(item.row, 1);
}).join('\n');
if (selection.length > 0) {
if(confirm("Are you sure you want to archive the following document(s)?\n" + docNames) == true) {
for (var i = 0; i < selection.length; i++) {
var item = selection[i];
var docurl = dt.getValue(item.row, 2);
var docname = dt.getValue(item.row, 1);
var folder = dt.getValue(item.row, 4);
return google.script.run.withSuccessHandler(onSuccessArchive).withFailureHandler(function(err) {
alert(err);
}).archiveDoc(docurl, folder);
}
}
archiveChart.getChart().setSelection([]);
}
});
});
I've got a tableview showing some data and I filter the shown data uisng UISearchbar. Each data struct consists of different values and
struct Cake {
var name = String()
var size = String()
var filling = String()
}
When a user starts typing I don't know whether he is filtering for name, size or filling. I don't want to use a scopebar. Is there a way to filter for various fields at the same time in swift 3?
This is the code I use to filter:
func updateSearchResults(for searchController: UISearchController) {
if searchController.searchBar.text! == "" {
filteredCakes = cakes
} else {
// Filter the results
filteredCakes = cakes.filter { $0.name.lowercased().contains(searchController.searchBar.text!.lowercased()) }
}
self.tableView.reloadData()
}
thanks for your help!
func updateSearchResults(for searchController: UISearchController)
{
guard let searchedText = searchController.searchBar.text?.lowercased() else {return}
filteredCakes = cakes.filter
{
$0.name.lowercased().contains(searchedText) ||
$0.size.lowercased().contains(searchedText) ||
$0.filling.lowercased().contains(searchedText)
}
self.tableView.reloadData()
}
I'm using Sharepoint to develop an open issues list for my company that will automatically track if an issue is about to be due or overdue based on the color of the row. I have a way to override the default Sharepoint list features and edit the rows, but I think my date comparisons aren't functioning correctly. Here is my code so far:
SP.SOD.executeFunc("clienttemplates.js", "SPClientTemplates", function() {
SPClientTemplates.TemplateManager.RegisterTemplateOverrides({
OnPostRender: function(ctx) {
var statusColors = {
'Almost Due' : '#FFFF00',
'Overdue' : '#FF0000',
};
var rows = ctx.ListData.Row;
for (var i=0;i<rows.length;i++)
{
var due = rows[i]["Due Date"];
var duedate = new Date(due);
var rowId = GenerateIIDForListItem(ctx, rows[i]);
var row = document.getElementById(rowId);
var today = new Date();
if(duedate <= today) {
var status = 'Overdue';
}
Else if (due.toDateString - today.toDateString <= 7 && due.toDateString - today.toDateString >= 0){
var status = 'Almost Due';
}
row.style.backgroundColor = statusColors[status];
}
}
});
});
I need the row to change red if the issue is past due and the issue to change yellow if the issue is due within one week.
Modified example:
SP.SOD.executeFunc("clienttemplates.js", "SPClientTemplates", function() {
SPClientTemplates.TemplateManager.RegisterTemplateOverrides({
OnPostRender: function(ctx) {
var statusColors = {
'Almost Due' : '#FFFF00',
'Overdue' : '#FF0000',
};
var rows = ctx.ListData.Row;
for (var i=0;i<rows.length;i++)
{
var dueDate = new Date(rows[i]["DueDate"]);
var today = new Date();
var status = null;
if(dueDate <= today) {
status = 'Overdue';
}
else if (daysBetween(today,dueDate) <= 7 && daysBetween(today,dueDate) >= 0){
status = 'Almost Due';
}
if (status != null){
var rowId = GenerateIIDForListItem(ctx, rows[i]);
var row = document.getElementById(rowId);
row.style.backgroundColor = statusColors[status];
}
}
}
});
});
function daysBetween(startDate, endDate) {
var timeDiff = Math.abs(endDate.getTime() - startDate.getTime());
var diffDays = Math.ceil(timeDiff / (1000 * 3600 * 24));
return diffDays;
}
The list of changes:
Fixed some typos like Else keyword
Introduced daysBetween function for getting dates difference in
days
due.toDateString - today.toDateString ...
I suspect that due.toDateString is returning a function definition. If you want your code to actually run that function, you'd want due.toDateString().
That said, since you're trying to do math with these dates, it's best to keep them as datetime objects or numbers rather than convert them to strings. Otherwise your subtraction is going to surprise you with NaN and your comparisons are going to be perpetually false.
If you subtract a date object from another date object in JavaScript, you'll get the difference between the dates in milliseconds. Convert that to days and you'll be good to go!