Setup:
I have a Google Form sending data to a Google Sheet. The Google Sheet does some calculations natively. I then use App Script to send the calculated values as a table in a Google Document.
Issue: there is no way (that I am aware of), to select a column of the "table" variable and apply attributes to the column only.
What I would like to do: something similar to table.setColumnAttributes(1, style)
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheetName = "DO NOT CHANGE"; // name of sheet to use
var sheet = ss.getSheetByName(sheetName);
//get the last r and column of data
var sheetLR = sheet.getLastRow();
var sheetLC = sheet.getLastColumn();
// define the range containing the data
// this can be changed depending on which columns to embed
var range = sheet.getRange(1,8,12,4);
// Logger.log("DEBUG: the range is "+range.getA1Notation());
// note - this is a change mentioned in
// Embed Google Spreadsheet table Withing Google Document when data contains dates
// when the data includes a data, "getValues()" fails hence
// changing the method to "getDisplayValues()"
var values = range.getDisplayValues();
// get the document details
var docid = "*************DOCID************";
var doc = DocumentApp.openById(docid);
var body = doc.getBody();
var ranges = doc.getNamedRanges('embeddedSheet-range');
if (ranges.length == 0) {
Logger.log("DEBUG: ranges is zero")
var table = body.appendTable(values);
}
else {
Logger.log("DEBUG: ranges is NOT zero")
tableRange = ranges[0];
table = tableRange.getRange().getRangeElements()[0].getElement();
var ind = body.getChildIndex(table);
tableRange.remove();
body.removeChild(table);
table = body.insertTable(ind, values);
table.setColumnWidth(0, 60);
table.setColumnWidth(2, 60);
table.setColumnWidth(3, 60);
var style = {};
style[DocumentApp.Attribute.FONT_FAMILY] = 'Calibri';
style[DocumentApp.Attribute.FONT_SIZE] = 18;
var test = table.getChild(1);
test.setAttributes(style);
}
var rangeBuilder = doc.newRange();
rangeBuilder.addElement(table);
doc.addNamedRange('embeddedSheet-range', rangeBuilder.build());
}
I don't see any default way to set a certain column's attributes (color, font size, etc). (You can natively set the column width using table.setColumnWidth, but not other attributes)
The following code (from above), highlights a row, but still lost on how to get a column
var style = {};
style[DocumentApp.Attribute.FONT_FAMILY] = 'Calibri';
style[DocumentApp.Attribute.FONT_SIZE] = 18;
var test = table.getChild(1);
test.setAttributes(style);
Let's examine "Document Structure". We see that the table object does not have direct column child object. So we should update style of each cell in a column consequently. For example, the first table and the 4-th column style is modified in the following way:
function setTableColProperties() {
const COL_INDEX = 3;
var style = {};
style[DocumentApp.Attribute.FONT_FAMILY] = 'Calibri';
style[DocumentApp.Attribute.FONT_SIZE] = 18;
style[DocumentApp.Attribute.FOREGROUND_COLOR] = '#0000cc';
var body = DocumentApp.getActiveDocument().getBody();
var table = body.getTables()[0]; // the table for styling
for (var i = 0; i < table.getNumRows(); i++) {
table.getCell(i, COL_INDEX).setAttributes(style);
}
}
table.getCell() method is useful to access the same column cells.
I have searched many possible answers but cannot seem to find one that works. I have a Google Sheet with about 1600 rows that I need to split into about 70 different tabs (with about 20-30 rows in each one) based on the value in the column titled “room”. I have been sorting and then cutting and pasting but for 70+ tabs this is very tedious.
I can use the Query function but I still need to create a new tab, paste the function and update the parameter for that particular tab.
This script seemed pretty close:
ss = SpreadsheetApp.getActiveSpreadsheet();
itemName = 0;
itemDescription = 1;
image = 2;
purchasedBy = 3;
cost = 4;
room = 5;
isSharing = 6;
masterSheetName = "Master";
function onOpen() {
var ui = SpreadsheetApp.getUi();
ui.createMenu('Update Purchases')
.addItem('Add All Rows To Sheets', 'addAllRowsToSheets')
.addItem('Add Current Row To Sheet', 'addRowToNewSheet')
.addToUi();
}
function addRowToNewSheet() {
var s = ss.getActiveSheet();
var cell = s.getActiveCell();
var rowId = cell.getRow();
var range = s.getRange(rowId, 1, 1, s.getLastColumn());
var values = range.getValues()[0];
var roomName = values[room];
appendDataToSheet(s, rowId, values, roomName);
}
function addAllRowsToSheets(){
var s = ss.getActiveSheet();
var dataValues = s.getRange(2, 1, s.getLastRow()-1, s.getLastColumn()).getValues();
for(var i = 0; i < dataValues.length; i++){
var values = dataValues[i];
var rowId = 2 + i;
var roomName = values[room];
try{
appendDataToSheet(s, rowId, values, roomName);
}catch(err){};
}
}
function appendDataToSheet(s, rowId, data, roomName){
if(s.getName() != masterSheetName){
throw new Error("Can only add rows from 'Master' sheet - make sure sheet name is 'Master'");
}
var sheetNames = [sheet.getName() for each(sheet in ss.getSheets())];
var roomSheet;
if(sheetNames.indexOf(roomName) > -1){
roomSheet = ss.getSheetByName(roomName);
var rowIdValues = roomSheet.getRange(2, 1, roomSheet.getLastRow()-1, 1).getValues();
for(var i = 0; i < rowIdValues.length; i++){
if(rowIdValues[i] == rowId){
throw new Error( data[itemName] + " from row " + rowId + " already exists in sheet " + roomName + ".");
return;
}
}
}else{
roomSheet = ss.insertSheet(roomName);
var numCols = s.getLastColumn();
roomSheet.getRange(1, 1).setValue("Row Id");
s.getRange(1, 1, 1, numCols).copyValuesToRange(roomSheet, 2, numCols+1, 1, 1);
}
var rowIdArray = [rowId];
var updatedArray = rowIdArray.concat(data);
roomSheet.appendRow(updatedArray);
}
But I always get an unexpected token error on line 51 or 52:
var sheetNames = [sheet.getName() for each(sheet in ss.getSheets())];
(And obviously the column names, etc. are not necessarily correct for my data, I tried changing them to match what I needed. Not sure if that was part of the issue.)
Here is a sample of my data: https://docs.google.com/spreadsheets/d/1kpD88_wEA5YFh5DMMkubsTnFHeNxRQL-njd9Mv-C_lc/edit?usp=sharing
This should return two separate tabs/sheets based on room .
I am obviously not a programmer and do not know Visual Basic or Java or anything. I just know how to google and copy things....amazingly I often get it to work.
Let me know what else you need if you can help.
Try the below code:
'splitSheetIntoTabs' will split your master sheet in to separate sheets of 30 rows each. It will copy only the content not the background colors etc.
'deleteTabsOtherThanMaster' will revert the change done by 'splitSheetIntoTabs'. This function will help to revert the changes done by splitSheetIntoTabs.
function splitSheetIntoTabs() {
var sheet = SpreadsheetApp.getActiveSheet();
var rows = SpreadsheetApp.getActiveSheet().getDataRange().getValues();
var header = rows[0];
var contents = rows.slice(1);
var totalRowsPerSheet = 30; // This value will change no of rows per sheet
//below we are chunking the toltal row we have into 30 rows each
var contentRowsPerSheet = contents.map( function(e,i){
return i%totalRowsPerSheet===0 ? contents.slice(i,i+totalRowsPerSheet) : null;
}).filter(function(e){ return e; });
contentRowsPerSheet.forEach(function(e){
//crate new sheet here
var currSheet = SpreadsheetApp.getActiveSpreadsheet().insertSheet();
//append the header
currSheet.appendRow(header);
//populate the rows
e.forEach(function(val){
currSheet.appendRow(val);
});
});
}
// use this function revert the sheets create by splitSheetIntoTabs()
function deleteTabsOtherThanMaster() {
var sheetNotToDelete ='Master';
var ss = SpreadsheetApp.getActive();
ss.getSheets().forEach(function(sheet){
if(sheet.getSheetName()!== sheetNotToDelete)
{
ss.deleteSheet(sheet);
}
});
}
I was using Kessy's nice script, but started having trouble when the data became very large, where the script timed out. I started looking for ways to reduce the amount of times the script read/wrote to the spreadsheet (rather than read/write one row at a time) and found this post https://stackoverflow.com/a/42633934
Using this principle and changing the loop in the script to have a loop within the loop helped reduce these calls. This means you can also avoid the second call to append rows (the "else"). My script is a little different to the examples, but basically ends something like:
`for (var i = 1; i < theEmails.length; i++) {
//Ignore blank Emails and sheets created
if (theEmails[i][0] !== "" && !completedSheets.includes(theEmails[i][0])) {
//Set the Sheet name = email address. Index the sheets so they appear last.
var currentSheet = theWorkbook.insertSheet(theEmails[i][0],4+i);
//append the header
//To avoid pasting formulas, we have to paste contents
headerFormat.copyTo(currentSheet.getRange(1,1),{contentsOnly:true});
//Now here find all the rows containing the same email address and append them
var theNewRows =[];
var b=0;
for(var j = 1; j < rows.length; j++)
{
if(rows[j][0] == theEmails[i][0]) {
theNewRows[b]=[];//Initial new array
theNewRows[b].push(rows[j][0],rows[j][1],rows[j][2],rows[j][3],rows[j][4],rows[j][5],rows[j][6],rows[j][7]);
b++;
}
}var outrng = currentSheet.getRange(2,1,theNewRows.length,8); //Make the output range the same size as the output array
outrng.setValues(theNewRows);
I found a table of ~1000 rows timed out, but with the new script took 6.5 secs. It might not be very neat, as I only dabble in script, but perhaps it helps.
I have done this script that successfully gets each room and creates a new sheet with the corresponding room name and adding all the rows with the same room.
function myFunction() {
var sheet = SpreadsheetApp.getActiveSheet();
// This var will contain all the values from column C -> Room
var columnRoom = sheet.getRange("C:C").getValues();
// This var will contain all the rows
var rows = SpreadsheetApp.getActiveSheet().getDataRange().getValues();
//Set the first row as the header
var header = rows[0];
//Store the rooms already created
var completedRooms = []
//The last created room
var last = columnRoom[1][0]
for (var i = 1; i < columnRoom.length; i++) {
//Check if the room is already done, if not go in and create the sheet
if(!completedRooms.includes(columnRoom[i][0])) {
//Set the Sheet name = room (except if there is no name, then = No Room)
if (columnRoom[i][0] === "") {
var currentSheet = SpreadsheetApp.getActiveSpreadsheet().insertSheet("No Room");
} else {
var currentSheet = SpreadsheetApp.getActiveSpreadsheet().insertSheet(columnRoom[i][0]);
}
//append the header
currentSheet.appendRow(header);
currentSheet.appendRow(rows[i]);
completedRooms.push(columnRoom[i][0])
last = columnRoom[i][0]
} else if (last == columnRoom[i][0]) {
// If the room's sheet is created append the row to the sheet
var currentSheet = SpreadsheetApp.getActiveSpreadsheet()
currentSheet.appendRow(rows[i]);
}
}
}
Please test it and don't hesitate to comment for improvements.
Pasted below is my script. I am copying a particular range of values from a sheet called "TalukaName" and copying it into "UserInterface!I2:I" cells. When I try to copy the values from the array into a Range, i get the error
Cannot convert Array to Object
at the line where I setValues of the Range. All my values that need to be copied into the Range are String values. Hence I added double quotes while creating array. Can anybody please help as to what is going wrong?
function test(){
var spreadsheet = SpreadsheetApp.getActive();
var ss = spreadsheet.setActiveSheet(spreadsheet.getSheetByName('UserInterface'), true);
var count = ss.getRange("H2").setFormula('=countif(TalukaName!B:B,UserInterface!C2)').getValue();
var DistrictName = ss.getRange('C2').getValue();
var matchindex = ss.getRange('H3').setFormula('=match(C2,TalukaName!B:B,0)').getValue();
var indexvalue = ss.getRange('H4').setFormula('=index(TalukaName!B:B,H3)').getValue();
var array = [];
var ssTaluka = spreadsheet.setActiveSheet(spreadsheet.getSheetByName('TalukaName'), true);
var range = ssTaluka.getDataRange();
var data = range.getValues();
for (var i = 0; i < count; i++) {
array[i]= '"' + data[i+matchindex-1][0] + '"';
}
ss.getRange('I2:I').setValues(array);
}
Not sure why this stopped working. I'm trying to get the latitude & longitude from an address. Was working at one point. Not sure what happened. I'm listing the address in column U.
function geocode(){
var tkhCopy = SpreadsheetApp.openById('xxx').getSheetByName('tkhCopy');
var range = tkhCopy.getRange('U2:U');
var addresses = range.getValues();
// Determine the first row and column to geocode
var row = range.getRow();
var column = range.getColumn();
// Set default destination columns
var destination = new Array();
destination[0] = 22; // column + 1;
destination[1] = 23; // column + 2;
// tkhCopy.insertColumnsAfter(column, 2);
var geocoder = Maps.newGeocoder();
var count = range.getHeight();
// Iterate through addresses and geocode
for(i in addresses) {
var location = geocoder.geocode(
addresses[i]).results[0].geometry.location);
tkhCopy.getRange(row, destination[0]).setValue(location.lat);
tkhCopy.getRange(row++, destination[1]).setValue(location.lng);
Utilities.sleep(200);
}
}
You could have hit a quota on the number of times you can use the Geocoder service. Wait a day and try again.
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]);
}
}
...