I tried to put together a script which would create calendar events only for the check marked rows in a spreadsheet, but if I had 3 rows checked, it would create 3 events based on the first checked row (so 3 same events).
Console log identifies the 3 different rows, but .createEvent takes the first checked row only, does not move onto the second and the third.
Here's the code, what have I messed up?
function createThickEvents() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("formhelper");
var data = sheet.getRange("A1").getDataRegion().getValues()
var approveData = data.filter(r => r[10] === true && r[11] === "")
var cal1 = "calendarID";
var cal2 = "calendarID";
var cal3 = "calendarID";
var cal4 = "calendarID"
var calendars = {
Name1: CalendarApp.getCalendarById(cal1),
Name2: CalendarApp.getCalendarById(cal2),
Name3: CalendarApp.getCalendarById(cal3),
Name4: CalendarApp.getCalendarById(cal4)}
approveData.forEach(r => {
var emailAddress = approveData[0][1];
var title = approveData[0][2];
var tstart = new Date(approveData[0][3]);
var tstop = new Date(approveData[0][4]);
var usingDays = approveData[0][6];
var remainingDays = approveData[0][9];
var desc = approveData[0][5];
var calComp = approveData[0][8];
var newEventComp = calendars[calComp].createEvent(title, tstart, tstop, {description:desc})
console.log(approveData)
})
}
I would do it like this:
function createThickEvents() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sh = ss.getSheetByName("formhelper");
const vs = sh.getRange("A1").getDataRegion().getValues();
const cal1 = "calendarID";
const cal2 = "calendarID";
const cal3 = "calendarID";
const cal4 = "calendarID"
const calendars = {
Name1: CalendarApp.getCalendarById(cal1),
Name2: CalendarApp.getCalendarById(cal2),
Name3: CalendarApp.getCalendarById(cal3),
Name4: CalendarApp.getCalendarById(cal4)
}
vs.forEach(r => {
if(r[10] === true && r[11] =="") {
let emailAddress = r[1];
let title = r[2];
let tstart = new Date(r[3]);
let tstop = new Date(r[4]);
let usingDays = r[6];
let remainingDays = r[9];
let desc = r[5];
let calComp = r[8];
calendars[calComp].createEvent(title, tstart, tstop, { description: desc })
}
})
}
Related
I have an issue that I cannot figure out. I wrote some code to take an item from a sheet called "Bag Label" and then look up its location in another sheet called "Inventory & Lots". and then to copy some of the cells to the right of that location.
It works well as long as the location is not row 80 or higher in sheet "Inventory & Lots". For the life of me, I cannot figure out why row 1 to 79 works but not 80+.
var ss=SpreadsheetApp.getActiveSpreadsheet();
var menuSheet=ss.getSheetByName("Bag Label");
var inventorySheet = ss.getSheetByName("Registered Lots");
var inventorySheetData = ss.getSheetByName("RegisteredLots - NO DEL");
var nextRow = inventorySheetData.getLastRow()+1;
var inventoryLots = ss.getSheetByName("Inventory & Lots");
var inventoryCalc = ss.getSheetByName("Inventory Calculation");
var inventoryCalcData = inventoryCalc.getRange(2,1,inventoryCalc.getLastRow()-1,4).getValues()
var inventoryData = inventoryLots.getRange(1,1,inventoryLots.getLastRow()-1,10).getValues()
var columnIndex = 0
var matchText = menuSheet.getRange("D2").getValue()
var kgNeed = menuSheet.getRange("AB2").getValue()
var index = inventoryData.findIndex(row => row[columnIndex] === matchText)
var rowNumber = index +1
var kg1 = inventoryLots.getRange(rowNumber,2).getValue();
var lot1 = inventoryLots.getRange(rowNumber,3).getValue();
var bb1 = inventoryLots.getRange(rowNumber,4).getValue();
var kg2 = inventoryLots.getRange(rowNumber,5).getValue();
var lot2 = inventoryLots.getRange(rowNumber,6).getValue();
var bb2 = inventoryLots.getRange(rowNumber,7).getValue();
var kg3 = inventoryLots.getRange(rowNumber,8).getValue();
var lot3 = inventoryLots.getRange(rowNumber,9).getValue();
var bb3 = inventoryLots.getRange(rowNumber,10).getValue();
Use array methods such as Array.filter() to get data more easily. Try this pattern to get started:
const ss = SpreadsheetApp.getActive();
const matchText = ss.getRange('Bag Label!D2').getValue();
const inventoryLots = ss.getSheetByName('Inventory & Lots');
const matchingData = inventoryLots.getDataRange().getValues()
.filter(row => row[0] === matchText);
I want users to search for their info from multiple sheet files and display multiple results boxes(row) but currently it's searching from one sheet and displaying only the first result(row) and not displaying the rest results.
What do I need to change in the script?
function submitData(obj){
var ss = SpreadsheetApp.openById("SHEETID");
var sheet = ss.getSheetByName("SHEETNAME");
var flag = 1 ;
var lr = sheet.getLastRow();
for(var i = 1;i <= lr;i++){
var id = sheet.getRange(i, 1).getValue();
if(id == obj){
flag = 0;
var B = sheet.getRange(i, 2).getValue();
var C = sheet.getRange(i, 3).getDisplayValue();
var data ="<div class="card"><h3>Info1:"+B+"</h3><p>Info2:"+C+"</p></div>";
return data;
}
}
if(flag==1){
var data = '<div class="carderror">Data not found</div>';
return data;
}
index.html
<input id="id" class="form-control">
<button type="submit" onclick="info()"> search</button>
<div id="result"></div>
<script>
function info () {
let obj = document.querySelector('#id').value;
let updateLocation = document.querySelector('#result');
updateLocation.innerHTML = "Searching...";
function onFailure(error){
let warning = error;
updateLocation.innerHTML = warning;
};
function onSuccess(response){
let result = response;
updateLocation.innerHTML = result;
};
google.script.run.withFailureHandler(onFailure)
.withSuccessHandler(onSuccess)
.submitData(obj);
};
</script>
You're only opening a single sheet file in your example. If you want to search multiple you'll have to specify which files you want to open and loop through them. My example below assumes all the files have the same sheet name.
Right now you're returning the first matched result, which is causing your search function to exit prematurely. To display multiple results you can push the matched lines to an array, then return that array once all the scanning is complete.
function submitData(obj) {
//specify what sheet IDs you want to search
var sheetNames = ['foo', 'bar', 'bla']
//create an array to push results to
var inject = []
for (var i = 0; i < sheetNames.length; i++) {
var sheetName = sheetNames[i]
var ss = SpreadsheetApp.openById(sheetName);
var sheet = ss.getSheetByName("SHEETNAME");
var flag = 1;
var lr = sheet.getLastRow();
for (var i = 1; i <= lr; i++) {
var id = sheet.getRange(i, 1).getValue();
if (id == obj) {
flag = 0;
var B = sheet.getRange(i, 2).getValue();
var C = sheet.getRange(i, 3).getDisplayValue();
var data = "<div class='card'><h3>Info1:" + B + "</h3><p>Info2:" + C + "</p></div>";
//push result to array
inject.push(data)
}
}
}
if (flag == 1) {
var data = '<div class="carderror">Data not found</div>';
return data;
} else {
//return joined array as string
return inject.join()
}
}
Multi-spreadsheet search
function MultiSearch() {
const ss = SpreadsheetApp.getActive();
const idsh = ss.getSheetByName("ssids");
const rsh = ss.getSheetByName("Results");
rsh.getRange(2,1,rsh.getLastRow() - 1, rsh.getLastColumn()).clearContent()
const ids = idsh.getRange(2,1,idsh.getLastRow() - 1,idsh.getLastColumn()).getValues().filter(r => r[2] == "TRUE").map(r => r[1]);
const r = SpreadsheetApp.getUi().prompt("Search Dialog","Enter Search String",SpreadsheetApp.getUi().ButtonSet.OK_CANCEL);
if(r.getSelectedButton() == SpreadsheetApp.getUi().Button.OK && r.getResponseText().length > 0) {
ids.forEach(id => {
let ss = SpreadsheetApp.openById(id);
let tf = ss.createTextFinder(r.getResponseText()).findAll();
tf.forEach(f =>{
rsh.appendRow([ss.getName,f.getSheet().getName(),f.getRow(),f.getColumn()]);
})
})
}
}
ssids Sheet:
Results Sheet:
Below I have working code that fills in the correct column that I want with the information that I want, the issue is I am going to have 2,000 rows I need to fill, and this fills in one cell at a time in descending order. Is there a way to do a batch update instead of doing 1 cell at a time in the column?
function logDataInEveryCell() {
const data = SpreadsheetApp.getActiveSpreadsheet().getRange("A2:A51").getValues();
let JOBID = [];
for (let a = 0; a < data.length; a++) {
JOBID = data[a]
var res = UrlFetchApp.fetch("API" + JOBID + "Token")
var content = res.getContentText();
var json = JSON.parse(content);
// Driver Name
var dispatches = json["dispatches"]["items"];
var lastItem = dispatches.pop();
var dName = [lastItem.vehicle.driver.contact.name]
Logger.log(dName)
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1");
var colValues = sheet.getRange("C2:C51").getValues();
var count = colValues.filter(String).length
sheet.getRange(count+2,3).setValue(dName);;
}}
Try this:
function logDataInEveryCell() {
const ss = SpreadsheetApp.getActive();
const vs = ss.getRange("A2:A51").getValues().flat();
const osh = ss.getSheetByName("Sheet1");
osh.clearContents();
let l = 1;
vs.forEach(e => {
let res = UrlFetchApp.fetch("API" + e + "Token");
let obj = JSON.parse(res.getContentText());
let items = obj["dispatches"]["items"];
items.pop();//guess
let vo = items.map(itm => [itm.vehicle.driver.contact.name])
osh.getRange(l,1,vo.length,vo[0].length).setValues(vo);
l += vo.length;
});
}
I have a script that creates a new file, names it and copies data from a template. The problem I am having is getting all of the formatting over such as merged cells. Here is the code I have so far.
function createNewSheet(e) {
const sh = e.range.getSheet();
var name = sh.getRange(e.range.rowStart, 1).getValue();
var crNew = SpreadsheetApp.create("Job Traveler-" + name);
var ssNew = SpreadsheetApp.openByUrl(crNew.getUrl()).getId();
importRange("1X4iOzc_shcOR8UM7OEM4F1-eR62YZmwKWyyEuu59Pf4","Job!A1:D", ssNew,"Sheet1!A1");
copiedFormat();
}
function importRange(sourceId,sourceRange,destinationID,destinationRangeStart){
const sourceSS = SpreadsheetApp.openById(sourceId);
const sourceRng = sourceSS.getRange(sourceRange);
const sourceVals = sourceRng.getValues();
// Get formatting data
const sourceFontStyle = sourceRng.getFontStyles();
const sourceFontWeight = sourceRng.getFontWeights();
const sourceFontBackgroundColor = sourceRng.getBackgrounds();
const destinationSS = SpreadsheetApp.openById(destinationID);
const destStartRange = destinationSS.getRange(destinationRangeStart);
const destSheet = destinationSS.getSheetByName(destStartRange.getSheet().getName());
destSheet.clear();
const destRange = destSheet.getRange(
destStartRange.getRow(),
destStartRange.getColumn(),
sourceVals.length,
sourceVals[0].length
);
destRange.setValues(sourceVals);
// Set formatting
destRange.setFontStyles(sourceFontStyle);
destRange.setFontWeights(sourceFontWeight);
destRange.setBackgrounds(sourceFontBackgroundColor);
SpreadsheetApp.flush();
}
Best
Description
I've only provided the addtional steps to duplicate merged cells from one range to another. I leave it to you to incorporate the relavent code to your script. I've included a debugging step 'merged.forEach()' which you can remove for production.
Script
function createNewSheet() {
try {
// in my case source and destination are the same spreadsheet but different sheets
// and I have made the ranges arbitrary
importRange("Sheet5","Sheet5!C6:L11","Sheet6","Sheet6!E11")
}
catch(err) {
console.log(err)
}
}
function importRange(sourceId,sourceRange,destinationID,destinationRangeStart) {
try {
var sourceSS = SpreadsheetApp.getActiveSpreadsheet();
var sourceRng = sourceSS.getRange(sourceRange);
var sourceVals = sourceRng.getValues();
var destinationSS = sourceSS;
var destStartRange = destinationSS.getRange(destinationRangeStart);
var destSheet = destinationSS.getSheetByName(destStartRange.getSheet().getName());
var row = destStartRange.getRow();
var col = destStartRange.getColumn();
var rows = sourceVals.length;
var cols = sourceVals[0].length;
destSheet.getRange(row,col,rows,cols).setValues(sourceVals);
var merged = sourceRng.getMergedRanges();
merged.forEach( range => console.log(range.getA1Notation()));
var rowOffset = destStartRange.getRow()-sourceRng.getRow();
var columnOffset = destStartRange.getColumn()-sourceRng.getColumn();
for( var i=0; i<merged.length; i++ ) {
row = merged[i].getRow()+rowOffset;
col = merged[i].getColumn()+columnOffset;
rows = merged[i].getNumRows();
cols = merged[i].getNumColumns();
destSheet.getRange(row,col,rows,cols).merge();
}
}
catch(err) {
console.log(err)
}
}
Reference
https://developers.google.com/apps-script/reference/spreadsheet/range#getMergedRanges()
https://developers.google.com/apps-script/reference/spreadsheet/range#merge()
I have a dropdown list that is in my workbook in the first tab (Tab titled: Reference Don't Edit) and I have a schedule template (aptly titled "Schedule Template").
I am trying to make the dropdown lists on all of the subsequent tabs (which would be daily tabs) instead of creating a new workbook everyday. I tried changing the dropSS and dropSS_ from using a sheet name to SpreadsheetApp.getActiveSpreadsheet().getSheets() and .getSheet(), etc for use on all active sheets, but it just erases the working dropdown and doesn't put it on the other sheets.
function createPrimaryDrpdwon() {
var dataSS = "References *Don't Edit*";
var dropSS = "Schedule Template";
var primaryDataRange = "A1:A46";
var primaryDropRange = "H4:H";
var primaryDropList = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(dataSS).getRange(primaryDataRange).getValues();
var primaryDropRange = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet().getRange(primaryDropRange);
var validationRule = SpreadsheetApp.newDataValidation().requireValueInList(primaryDropList).build();
primaryDropRange.setDataValidation(validationRule);
}
function createSecondaryDrpdwon(e){
var dataSS = "References *Don't Edit*";
var dropSS = "Schedule Template";
var dropSS_ = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(dropSS);
var allDataRange = "A1:B46";
var primaryDDCol = 8;
var dropDData = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(dataSS).getRange(allDataRange).getValues();
var activeCell = dropSS_.getActiveCell();
var activeColumn = activeCell.getColumn();
var activeRow = activeCell.getRow();
if(activeColumn==primaryDDCol){
var dep_Col = primaryDDCol+1;
var dep_Row = activeRow;
var depCell = dropSS_.getRange(dep_Row, dep_Col);
var primarySelected = activeCell.getValue();
var validationRule = SpreadsheetApp.newDataValidation().requireValueInList(getDependentList(dropDData,primarySelected)).build();
depCell.setDataValidation(validationRule);
}
}
function getDependentList(dropDData,primarySelected){
var dependenList = [];
var j = 0;
if(dropDData != null){
for(i=0; i<dropDData.length; i++){
if(dropDData[i][0]==primarySelected){
dependenList[j] = dropDData[i][1];
j++;
}
}
}
return dependenList;
}
Try this:
function createPrimaryDrpdwon() {
var dataSS = "References *Don't Edit*";
var dropSS = "Schedule Template";
var primaryDataRange = "A1:A46";
var primaryDropRange = "H4:H";
const ss = SpreadsheetApp.getActive();
var primaryDropList = ss.getSheetByName(dataSS).getRange(primaryDataRange).getValues().map(r => r[0]).filter(e => e);
var primaryDropRange = ss.getActiveSheet().getRange(primaryDropRange);
var validationRule = SpreadsheetApp.newDataValidation().requireValueInList(primaryDropList).build();
primaryDropRange.setDataValidation(validationRule);
}
function getDependentList(dropDData, primarySelected) {
var dependenList = dropDData.map(r => {
if (r[0] == primarySelected) {
return r[1];
} else {
return '';
}
}).filter(e => e);
return dependenList;
}