How to set the data rapidly in google apps script? - google-apps-script

I'm a junior developer in the HR system.
Let's assume the below.
There are two sheets 'A' and 'B'.
I want to copy the contents of 'B' to 'A'.
So I selected the method using getRange, getValues, setValue.. etc.
The method was successful, but the processing speed failed.
The speed was very very slow.
How can I set the data more rapidly?
I wrote the code below.
Please help me with your advice!
function setList(){
var spreadSheet = SpreadsheetApp.getActiveSpreadsheet();
fromSheet = spreadSheet.getSheetByName('B');
toSheet = spreadSheet.getSheetByName('A');
var arry = fromSheet.getRange('A1:M239').getValues();
Logger.log(arry);
var lastRow = fromSheet.getLastRow();
var lastCol = fromSheet.getLastColumn();
for (var j = 2; j< lastRow; j++){
for (var i = 1; i < lastCol; i++){
toSheet.getRange(usedRow + 1,i).setValue(fromSheet.getRange(usedRow + 1,i).getValue());
}
usedRow++;
}
}

You were on the right track at first with getValues() but then you did each cell individually with getValue()/setValue(). Since you want to copy all the values from fromSheet to toSheet just use 1 setValues().
function setList(){
var spreadSheet = SpreadsheetApp.getActiveSpreadsheet();
fromSheet = spreadSheet.getSheetByName('B');
toSheet = spreadSheet.getSheetByName('A');
var arry = fromSheet.getRange('A1:M239').getValues();
Logger.log(arry);
array.shift(); // remove the headers
// you don't define usedRow so I'm assuming its the same row as fromSheet
toSheet.getRange(2,1,arry.length,arry[0].length).setValues(arry);
}
Reference
Sheet.getRange()
Range.getValues()
Range.setValues()
Best Practices

Related

Is there a better way to make a copy of a spreadsheet with content only?

I've been trying to copy only the content (no equations such as =IMPORTRANGE) of a spreadsheet into a new spreadsheet. I have found a solution however I was wondering if there was an easier way to do this?
I ended up iterating through each sheet in the original spreadsheet, getting the data range of each sheet, getting those values, copying each sheet into the new spreadsheet with .copyTo, deleting the "Sheet1" created with the new spreadsheet, then iterating through the new spreadsheet and pasting the values to each new sheet in the spreadsheet.
function copySpreadsheetContent() {
var ss1 = SpreadsheetApp.getActiveSpreadsheet();
var sheetNums = ss1.getNumSheets();
var ssNew = SpreadsheetApp.create('Test Spreadsheet').getId();
var ss2 = SpreadsheetApp.openById(ssNew);
var ss2Nums = ss2.getNumSheets();
var sheetVals = [];
for(var i = 0; i < sheetNums; i++){
var sheet = ss1.getSheets()[i];
sheetVals[i] = sheet.getDataRange().getValues();
var newSheet = sheet.copyTo(ss2);
newSheet.setName(sheet.getName());
}
var sheet1 = ss2.getSheetByName("Sheet1");
ss2.deleteSheet(sheet1);
for(var j = 0; j <= ss2Nums; j++){
var ss2Sheet = ss2.getSheets()[j];
ss2Sheet.getDataRange().setValues(sheetVals[j]);
}
}
This provides the desired results as far as I can tell, however it just seems like a really roundabout way of doing things and I was hoping someone might be able to point me to a more efficient or straightforward method.
Thanks for the help
My coworker ended up finding a better way to do this with a single for loop and the ability to send it to a folder in your drive
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var destFolder = DriveApp.getFoldersByName("Experiments").next();
var file = DriveApp.getFileById(spreadsheet.getId()).makeCopy("Saved Copy", destFolder);
var newSpreadsheet = SpreadsheetApp.open(file);
for(var i = 0; i < spreadsheet.getNumSheets(); i++){
newSpreadsheet.getSheets()[i].getDataRange().setValues(spreadsheet.getSheets()[i].getDataRange().getValues());
}

Google Apps Script to list all Google Classroom courses and put them in a Google Spreadsheet

I am trying to create a Google Apps Script that will list all the Google Classroom courses that are active and archived, along with the ID, NAME, SECTION, and COURSESTATE. Everything works except that I have no idea how to fix the .getRange so that it will put all the information in the Google Spreadsheet. The error I get is "Incorrect range height, was 4 but should be 10". If I put the .getRange as simply "A1:D1", it will overwrite what is there for each Class until the script finishes, so I know the rest of the script works, but I can't figure out how to put the range so that all the Classes can be listed. What I have up to now is this:
function listCourses() {
var response = Classroom.Courses.list();
var courses = response.courses;
if (courses && courses.length > 0) {
for (i = 0; i < courses.length; i++) {
var course = courses[i];
var ids = course.id;
var title = course.name;
var sec = course.section;
var state = course.courseState;
var arr1 = [];
arr1.push(ids,title,sec,state);
var arr2 = [];
while(arr1.length) arr2.push(arr1.splice(0,1));
var s = SpreadsheetApp.getActiveSpreadsheet();
var sh = s.getSheetByName('LISTS');
for (var x=0; x<arr2.length; x++){
var newRow = sh.getLastRow() + 1;
// Here in the .getRange is where it gives me the error. A new row should be added for each Class until all the Classes (with the information mentioned above) are listed.
sh.getRange(1, 1, newRow, arr2[0].length).setValues(arr2);
}
}}}
Try something like this:
I don't use Classroom API much and I only have one class in it but this works for it.
function listCourses() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sh = ss.getSheetByName('LISTS');
var response = Classroom.Courses.list();
var courses = response.courses;
var arr=[];//You could put column headers in here
for (i = 0; i < courses.length; i++) {
var course = courses[i];
var ids = course.id;
var title = course.name;
var sec = course.section;
var state = course.courseState;
arr.push([title,sec,state]); //you could also go sh.appendRow([title,sec,state]); here if you wish and avoid the use of the two dimensional array all together as suggested in the other answer.
}
sh.getRange(1, 1, arr.length, arr[0].length).setValues(arr);
}
Jason. You could bypass the range issue by using append row. This will however mean manipulating the data so that each class's details is on one row. I prefer this approach because it is let prone to errors. Also it automatically finds the next empty row in the sheet, so you don't need to get the last row.
function appendRow() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Sheet1');
// Try is added to catch failures on automated scripts
try {
sheet.appendRow(['cell1','cell2','cell3'])
} catch (e){
Logger.log(e)
// Could send an email here to alert script failure
}
// In a loop
var data = [['row 1 cell 1','row 1 cell 2'],['row 2 cell 1','row 2 cell 2 ']]
data.forEach(function(item){
sheet.appendRow(item)
})
}

Permission problems when altering a spreadsheet

I have this piece of code that should set a custom format number depending on the value of the cell.
function fixFormat() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0];
var range = sheet.getRange("K1:K30");
var rows = range.getNumRows();
var cols = range.getNumColumns();
for(var row = 1; row <= rows; row++) {
for(var col = 1; col <= cols; col++) {
var cell = range.getCell(row, col);
var value = cell.getValue();
if (value > 1000) {
cell.setNumberFormat("#.###B");
} else {
cell.setNumberFormat("#.###");
}
}
}
}
When configured as a trigger (onChange) it does not run at all, when called as =fixFormat() I get the error: You don't have permission to call setNumberFormat.
I am the owner of the sheet
I have not blocked access to anything
I had the same script working on other sheets
What can I do to solve this?
Found the problem
After a lot of digging, the support page says:
Spreadsheet: Read only (can use most get*() methods, but not set*()). Cannot open other spreadsheets (SpreadsheetApp.openById() or SpreadsheetApp.openByUrl()).
So the solution (barely) is to use custom menus

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]);
}
//}