I have a spreadsheet A which is the source of data and spreadsheet B where this data should be put to. The data needs to be transposed, but there's also a number of conditions like Month, Name and Attribute. So there's 3 matching parameters to meet(Name, Month, Attribute). And this is what I can't get my head around.
This is the code I've come up with, but it's no good since the transpose function doesn't work at all. And how to apply matching to this code is hard for me.
function transpose()
var sourcess = SpreadsheetApp.openById();
var sourcesheet = sourcess.getSheetByName();
var sourcerange = sourcesheet.getRange((row2col()));
var sourcevalues = sourcerange.getValues();
var ss = SpreadsheetApp.getActiveSpreadsheet();
var destsheet = ss.getSheetByName();
var destrange = destsheet.getRange();
destrange.setValues(sourcevalues);
function row2col(row) {
return row[0].map(function(elem) {return [elem];});
Any help is very much appreciated.
Here is a test file of what this should look like
Related
I have a script that takes data from a gsheet and replaces placeholders on gdoc. I am looking to optimise the script by using arrays instead.
This is a sample of my gsheet (the original gsheet spans 1000+ rows and 15+ columns),
Original script:
function generategdoc() {
SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1").activate();
var ss = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var lr = ss.getLastRow();
for (var i =2;i<=lr;i++){
if(ss.getRange(i, 1).getValue()){
//Make a copy of the template file
var documentId = DriveApp.getFileById('FileID').makeCopy().getId();
var Client = ss.getRange(i, 2).getValue();
var Amount = ss.getRange(i, 3).getValue();
var AmountFormat = Amount.toFixed(2).replace(/(\d)(?=(\d{3})+\.)/g, '$1,');
var Date = ss.getRange(i, 4).getValue();
var temp = new Date(Date)
var DateFormat = Utilities.formatDate(temp, "GMT+0400", "dd MMM yyyy")
//Rename the copied file
DriveApp.getFileById(documentId).setName(Client);
//Get the document body as a variable
var body = DocumentApp.openById(documentId).getBody();
body.replaceText('##Client##', Client).replaceText('##Amount##', AmountFormat).replaceText('##Date##', DateFormat)
}
else {}
}
}
As you can see this script will only run for all the rows which have been checkboxed TRUE.
Attempt 1 at optimising:
function optimise() {
SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1").activate();
var ss = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var lr = ss.getLastRow();
var rng = ss.getRange("A1:"+"D"+lr).getValues(); //Creation of Array
for (var i =2;i<=lr;i++){
if(ss.getRange(i, 1).getValue()){
var Client = rng[i-1][1];
var Amount = rng[i-1][2];
var Date = rng[i-1][3];
var documentId = DriveApp.getFileById('FileID').makeCopy().getId();
DriveApp.getFileById(documentId).setName(Client);
var body = DocumentApp.openById(documentId).getBody();
body.replaceText('##Client##', Client).replaceText('##Amount##', Amount).replaceText('##Date##', Date)
}
else {}
}
}
Question:
I was able to format the original script for Amount and Date. How can I have the same formatting for arrays? As I cant apply formatDate(class utilities) and toFixed to my variables anymore because they are now arrays.
I believe you can optimize your code by using the appropriate method(s) to preserve the text format of your Google sheet values.
Look into using the following method(s) of class Range from SpreadsheetApp service.
Method getDisplayValue() returns the displayed value of the cell in the range. The value is a String. The displayed value takes into account date, time and currency formatting, including formats applied automatically by the spreadsheet's locale setting. Empty cells return an empty string.
This should optimize your code by removing the necessity of regex. If I didn't understand your issue correctly, please leave a comment.
References:
getDisplayValue()
getDisplayValues()
getRichTextValue()
getRichTextValues()
You can try something like this. But it may not make any difference because creating files takes a long time.
function optimise() {
var ss = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var vA= ss.getRange(1,1,ss.getLastRow(),4).getValues();
var file = DriveApp.getFileById('FileID');
for(var i=0;i<vA.length;i++){
if(vA[i][0]){
var Client=vA[i][1];
var Amount=vA[i][2];
var Date=vA[i][3];
DriveApp.getFileById(file.makeCopy().getId()).setName(Client);
var body=DocumentApp.openById(documentId).getBody();
body.replaceText('##Client##', Client).replaceText('##Amount##', Amount).replaceText('##Date##', Date)
}
}
}
I wonder if you need to saveAndClose() the document.
Ok...not sure how to do this. Right now I 4 sheets and 4 scripts for each sheet producing 4 json feeds. What I am trying to experiment with is having one script that will produce 1 json that I can use in a web page and just call the type of class. They are all formatted the same with columns etc.
Here is the Google Script App code I have.
function doGet(e){
// Change Spread Sheet url
var ss = SpreadsheetApp.openByUrl("https://docs.google.com/spreadsheets/d/SpreadsheetID/edit#gid=0");
// Sheet Name, Change Sheet to whatever name you used to name your sheet
var sheet = ss.getSheetByName("Class Sheet 1");
return getClasses(sheet);
}
function getClasses(sheet){
var dataObj = {};
var dataArray = [];
// collecting data starting from 2nd Row , 1st column to last row and last column
var rows = sheet.getRange(2,1,sheet.getLastRow()-1, sheet.getLastColumn()).sort([{column: 1, ascending: true}, 1]).getValues();
for(var i = 0, l= rows.length; i<l ; i++){
var dataRow = rows[i];
var record = {};
record['Name'] = dataRow[0];
record['Note'] = dataRow[1];
record['Address'] = dataRow[2];
record['StreetAddress'] = dataRow[3];
record['City'] = dataRow[4];
record['State'] = dataRow[5];
record['ZipCode'] = dataRow[6];
record['ContactName'] = dataRow[7];
record['EMailAddress'] = dataRow[8];
record['CustomerServicePhone'] = dataRow[9];
dataArray.push(record);
}
dataObj = dataArray;
var result = JSON.stringify(dataObj);
return ContentService.createTextOutput(result).setMimeType(ContentService.MimeType.JSON);
}
Scratching my head on this a little bit....I'm sure its something simple and I am probably over thinking things, but any help would be appreciated.
Possible Solution:
The e object in your doGet(e) provides a way to send parameters to your script. You can access different sheets with different url parameters. You can then easily get the requested SheetName through e.parameter. Use
https://script.google.com/.../exec?sheet=ClassSheet1 //for ClassSheet1
https://script.google.com/.../exec?sheet=ClassSheet2 //for ClassSheet2
Code.gs:
function doGet(e){
var ss = SpreadsheetApp.openByUrl("https://docs.google.com/spreadsheets/d/SpreadsheetID/edit#gid=0");
var sheetName = e.parameter.sheet;
var sheet = ss.getSheetByName(sheetName);
return getClasses(sheet);
}
You can also provide UI in your web-app to select a sheet.
I've read through this post >> set format as plain text
I'm creating a new thread because that thread is a few years old already and I didn't want to revive something from a few years ago.
the code:
function A1format() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var mainsheet = ss.getSheetByName("Sheet1");
var G = mainsheet.getRange("C15:BH3000").getGridId();
var illa = mainsheet.getRange("A13");
Logger.log(G);
illa.copyFormatToRange(G, 16, 3,200, 30);
}
This code is supposed to set plain text format for the sheet named Sheet1
I've tried var mainsheet = ss.getSheetByName("Sheet1, Sheet2, Sheet3"); but this doesn't seem to work, I just get an error message.
This is the current code I have, this code works but is both inefficient and a real pain to maintain if something changes:
function setPlainTextDefault() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet1 = ss.getSheets()[0];
var sheet2 = ss.getSheets()[2];
var sheet3 = ss.getSheets()[4];
var sheet4 = ss.getSheets()[7];
var sheetColumn1 = sheet1.getRange("A1:A");
var sheetColumn2 = sheet2.getRange("A1:A");
var sheetColumn3 = sheet3.getRange("A1:A");
var sheetColumn4 = sheet4.getRange("A1:A");
sheetColumn1.setNumberFormat("#");
sheetColumn2.setNumberFormat("#");
sheetColumn3.setNumberFormat("#");
sheetColumn4.setNumberFormat("#");
}
Here I am changing each column A in every sheet to plain text by using the index number of the sheet, so I have to manually count the number for every sheet, this is a nightmare as I have a very large number of sheets, it will take too much time for me to manually count the sheets and then add it to my current code. I know there is a better more efficient way of doing this, but I don't know how due to my lack of knowledge in google apps scripting.
How do you do this for every sheet in the document regardless of how many sheets are present? I want to go through every sheet, from sheet1 till x number of sheets and then change every column A to plain text.
var mainsheet = ss.getSheetByName("Sheet1, Sheet2, Sheet3") throws an error because there isn't a sheet named "Sheet1, Sheet2, Sheet3". "There isn't a way to reduce the calls to the Spreadsheet Service on your code, but you could make that it "looks better" by using some JavaScript features like loops and array handling.
Example (untested)
On the following code snippet, arrays, a for and indexOf are used to reduce a bit the number of lines of the original code.
function setPlainTextDefault() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheets = ss.getSheets();
var sheetsToProcess = [0,2,4,7]; // Array holding the sheets indexes to process
for(var i = 0; i < sheets.length; i++){
/* If i is not in the sheetsToProccess indexOf returns -1 which is parsed as false
otherwise the result is parsed as true */
if(sheetsToProcess.indexOf(i)){
var sheet = ss.getSheets()[i];
var sheetColumn = sheet.getRange("A1:A");
sheetColumn.setNumberFormat("#");
}
}
}
To make the above work for every sheet, comment out or remove if(sheetsToProcess.indexOf(i)){ and the corresponding }.
It's worth to note that if you are looking help to find the "best" way to improve your code, you could try Code Review.
This was the code I needed:
function setPlainText() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheets = ss.getSheets();
for(var i = 0; i < sheets.length; i++) {
//Logger.log(sheets[i].getName());
var setPlainText = ss.getSheets()[i];
var sheetColumnA = setPlainText.getRange("A1:A");
sheetColumnA.setNumberFormat("#");
var sheetColumnB = setPlainText.getRange("B1:B");
sheetColumnB.setNumberFormat("#");
}
}
It will:
Iterate through a document with x number of sheets however big or small
Then for each iteration, set plain text format for every column A and B for every sheet in the document
This function/trigger combo is intended to keep a log of live data on a daily basis. The intent is that N2:P2 get pasted as values at the bottom of Columns C:E.
pullvalue() works fine if I run it manually.
HERE'S THE ISSUE: Setting up a time-driven trigger causes the values to be pasted in at C1:E1 instead of at the bottom of the columns.
My suspicion is that target_range is breaking down when Clast returns a zero value, but I can't figure out why.
What am I missing? Thanks for your help/feedback!
function pullvalue() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var target = SpreadsheetApp.getActiveSpreadsheet();
var source_sheet = ss.getSheetByName("Production");
var target_sheet = target.getSheetByName("Production");
var source_range = source_sheet.getRange("N2:P2");
var Cvals = ss.getRange("C1:C").getValues();
var Clast = Cvals.filter(String).length;
var target_range = target_sheet.getRange("C"+(Clast+1)+":E"+(Clast+1))
source_range.copyTo(target_range, {contentsOnly:true});}
Please verify this line:
var Cvals = ss.getRange("C1:C").getValues();
variable ss return currently activespreadsheet only, but when it runs from a time-driven trigger there is no active sheet, so you need to indicate the sheet, you can use getSheetByName() or getSheets() the line should look like this:
var sheet = ss.getSheetByName("Sheet1");
var Cvals = sheet.getRange("C1:C").getValues();
Hope this will help you.
Thanks.
I have a google form which populates a google spreadsheet (source sheet). I located and customized a script to pull certain columns -in their entirety- from the source sheet into a new tab/sheet. Apparently there has to be the same number of rows in both sheets for the script to run properly. If not, it turns back an error. Every time a new form is submitted, a row is added to the source sheet, putting the two sheets out of sync and breaking the script.
I'd like help figuring out what function I need to add to the existing script (or what changes I can make to it) so when a new row appears in the source sheet (because a form has been submitted), a blank row appears in the target sheet.
Do I need to tweak my script or add a new function?
function importFunction(a) {
var ss = SpreadsheetApp.openById("0ApTaY3v27-UqdElwZTBvanNpaC1UckpxaTJRZS1XNWc");
var sourceSheet = ss.getSheets()[0];
var ss2 = SpreadsheetApp.openById("0ApTaY3v27-UqdElwZTBvanNpaC1UckpxaTJRZS1XNWc");
var targetSheet = ss2.getSheets()[1];
var values1 = sourceSheet.setActiveSelection("C:C").getValues();
var values2 = sourceSheet.setActiveSelection("AD:AD").getValues();
var values3 = sourceSheet.setActiveSelection("D:E").getValues();
var values4 = sourceSheet.setActiveSelection("AE:AE").getValues();
var values5 = sourceSheet.setActiveSelection("F:H").getValues();
var values6 = sourceSheet.setActiveSelection("N:U").getValues();
targetSheet.setActiveSelection("A:A").setValues(values1);
targetSheet.setActiveSelection("B:B").setValues(values2);
targetSheet.setActiveSelection("C:D").setValues(values3);
targetSheet.setActiveSelection("E:E").setValues(values4);
targetSheet.setActiveSelection("F:H").setValues(values5);
targetSheet.setActiveSelection("I:P").setValues(values6);
}
Per the suggestion below, I tried to change script to the following, but I get an error - Cannot find method appendRow(). How do I fix that?
function importFunction(a) {
var ss = SpreadsheetApp.openById("0ApTaY3v27-UqdElwZTBvanNpaC1UckpxaTJRZS1XNWc");
var sourceSheet = ss.getSheets()[0];
var ss2 = SpreadsheetApp.openById("0ApTaY3v27-UqdElwZTBvanNpaC1UckpxaTJRZS1XNWc");
var targetSheet = ss2.getSheets()[1];
var targetMax = targetSheet.getMaxRows();
var values1 = sourceSheet.setActiveSelection("C:C").getValues();
var values2 = sourceSheet.setActiveSelection("AD:AD").getValues();
var values3 = sourceSheet.setActiveSelection("D:E").getValues();
var values4 = sourceSheet.setActiveSelection("AE:AE").getValues();
var values5 = sourceSheet.setActiveSelection("F:H").getValues();
var values6 = sourceSheet.setActiveSelection("N:U").getValues();
if(targetMax == values1.length) {
targetSheet.setActiveSelection("A:A").setValues(values1);
targetSheet.setActiveSelection("B:B").setValues(values2);
targetSheet.setActiveSelection("C:D").setValues(values3);
targetSheet.setActiveSelection("E:E").setValues(values4);
targetSheet.setActiveSelection("F:H").setValues(values5);
targetSheet.setActiveSelection("I:P").setValues(values6);
}
else
targetSheet.appendRow();
}
You could simply check the number of available rows in sheet SS2 before writing the values using getMaxRows() and compare it to the length of your data arrays value1.length, then, depending on this comparison add the rows you need using appendRow() eventually in a for next loop.
EDIT : following your EDIT, I tested a bit further and it appears that appendRow() doesn't work as I thought, it appends Rows at the begining of an empty sheet... so I tried this :
if(targetMax < values1.length) {
var RowsToAdd = values1.length-targetMax
for(nn=0;nn<RowsToAdd;++nn){targetSheet.insertRowAfter(targetMax)}
}
Just place it right after var value6=...
sorry for this little error ;-)