Can't pass variable to .getRange() - javascript

I'm trying to write a script that gets a range who's size is based on a constant. My the code I've been trying is below
//Save range based on number of components
var numComponents = 3;
var compRange = SpreadsheetApp.getActiveSheet().getRange(15,3,numComponents,3);
var compValues = compRange.getValues();
var components = "";
//Convert the components into paragraph form
for (var row = 0; row <= numComponents ;row++){
components = components + compValues[row][1] + " " + compValues[row][2] + " "+ compValues[row][0]+ "\n";
};
Whenever I run this code, I get an error saying
Error: Cannot read property 1 of undefined
When I replace numComponents with 3, the code works perfectly, however that will not always be the case. How can I make it work properly?

It's very likely that the problem is this expression row <= numComponents
Replace
for (var row = 0; row <= numComponents ;row++){
by
for (var row = 0; row < numComponents ;row++){

Related

How do I limit the characters pulled from getBody in apps script

I'm trying to pull data from gmail (ie. from, to, date, subject, body, etc.) but I'm getting an error saying the getBody() length is too long:
your input contains more than the maximum of 50000 characters in a single cell
I'm trying to have a cut off of characters or just not pull the email with a Body over the character limit. Also, I'm pretty out of practice on this and it's not working...
//Allow user to select label(s) and date range
function getGmailEmails() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var label = sheet.getRange(1, 3).getValue();
var after = sheet.getRange(2, 3).getDisplayValue();
var before = sheet.getRange(3, 3).getDisplayValue();
var threads = GmailApp.search("label:" + label + " AND " + "after:" + after + " AND " + "before:" + before);
// Export emails into table of values in google sheet
// Character length cutoff is 2000, the cell with get Body would tell you to go to the inbox
var values = [];
for (var i = 0; i < threads.length < 2000; i++) {
var temp = [];
var label = threads[i].getLabels().map(e => e.getName()).join(", ");
let messages = threads[i].getMessages();
for (var j = 0; j < messages.length; j++) {
temp.push([
label,
messages[j].getFrom(),
messages[j].getTo(),
messages[j].getCc(),
messages[j].getDate(),
messages[j].getSubject(),
messages[j].getBody(),
messages[0].getId()
]);
}
values = values.concat(temp);
}
sheet.getRange(6, 1, values.length, values[0].length).setValues(values);
}
One way is by using String.prototype.slice().
Replace
messages[j].getBody(),
by
messages[j].getBody().slice(0,50000),
Related
Substring vs Slice vs Other?

Google sheets script - Strip extra data from a second line and add it to the first line

I'm trying to build a script in google sheets script that will iterate down a sheet, row by row, and if it encounters an ID number in the first cell of that row that is identical to the one above it, it strips out the data from every cell EXCEPT Column A and B, and appends it to the line above. Ideally, this would work with an indeterminate number of duplicate row IDs, might be 2, might be 3, might be 4.
After stripping out the data I want to keep (eg colums C and onward), I then want to delete the entire contents of the processed duplicate ID row, but I just haven't put that in my script until after it copies the data correctly.
In this example, sheet rows 6, 7 and 8 have identical ID numbers (Column A)
Here is the result I'm trying to get:
And here is the result I'm getting:
I've tried a number of different ways, and torn down and rebuilt my script a couple of times without getting the result I want:
function stripMiner() {
var ss = SpreadsheetApp.openById("1WDPoTICQvdruxfhAwHLtA51fz05DqyZ-NhNfpAyPO6Y");
var mainSheet = ss.getSheetByName("Main");
var startRow = 5;
var numRows = mainSheet.getLastRow();//obtains the last row in the sheet
var setrgh = mainSheet
var dataRange = mainSheet.getRange(startRow, 1,4,120); //rowStart, columnStart, row count, column count, the columncount needs to be large enough to encompass all your ancillary data
var data = dataRange.getValues();
var iter = 0;
var maxItRow = 4;
var prevIdNum = 0;
var dupCount = 1;
var cc1 = "P5"; //Cells to dump check values into
var cc2 = "P6";
var dumpRow = startRow;
//if (numRows >= maxItRow){var maxIter = maxItRow;}
for (i in data){
if (iter != maxItRow){ //making sure we haven't gone over the iteration limit
var row = data[i];
var idNum = (row[0]);
var jCount = 0; //resets icount if the id number is different icount is used to skip some cells in a row
if (idNum == prevIdNum){//only proceed if we've hit another line with the same ID number
dupCount = +1; //increment the dupcount value
mainSheet.getRange(cc2).setValue("dupCount"+dupCount); //dupcount check value
var rowIterStart = 5; //RowIterStart is used to add to rowiter, EG if your data is 20 columns wide, and you start transposing from column 4, then this will want to be about 17
var rowIter = 1;
for (j in row){
if (jCount >= 2){ //the integer here is the column where it will begin to transpose data
mainSheet.getRange(dumpRow-1,(rowIterStart*dupCount)+(rowIter)).setValue(row[j]); //startRow+(iter-dupCount)
mainSheet.getRange(cc1).setValue("dumprow"+dumpRow);
}
rowIter+=1;
jCount +=1;
}
}
else{
var dupCount = 1;
dumpRow +=1;
}
prevIdNum = (row[0]); //sets the most recently processed rows ID number
}
iter +=1;
}
}
I'm not quite sure where I'm going wrong. Does anyone have any suggestions? Thanks!
(Also I'm still just a beginner with this so if I've overlooked anything obvious or taken the wrong approach to do this, I apologize!)
The results for the questioner's code in the case of copied data arise from a convoluted loop. In essence, though duplicates were identified, there was a mis-counting to assign the copied data to the correct rowID. So far as clearing data, no provision was included.
The following code works to meet the questioner's goals, though it is far from perfect.
At present, the recalculation of the "last column" after copy each duplicate is an absolute rather than than a row-based figure. So, if a duplicate was detected for, say, ID=3, the data would be copied to column 12 rather than column 6. This requires the addition of a simple dupID row counter.
The second factor is the calculation of the last column in the spreadsheet.
var dataRange = mainSheet.getRange(startRow, 1,Rowlast+1,120);
The questioner used 120 columns; and I have retained that number simply for the sake of consistency. The questioner should re-assess whether this is excessive.
function ejb_so_5284922701() {
var ss = SpreadsheetApp.openById("<< insert questioners spreadsheet ID>>");
var mainSheet = ss.getSheetByName("<< insert questioner's sheet name >>");
var startRow = 5;
// calculate the last row containing data
var Rowvals = ss.getRange("A5:A").getValues();
var Rowlast = Rowvals.filter(String).length; //6
Logger.log("last row = " + Rowlast); // DEBUG
// calculate the last column containing data
var cell = mainSheet.getRange("A5"); //or however you determine "cell"
var drCol = mainSheet.getDataRange().getLastColumn();
Logger.log('getLastColumn = ' + drCol); //DEBUG
for (var i = drCol; i >= 1; i--) {
if (mainSheet.getRange(cell.getRow(), i).getValue() != "") {
break;
}
}
var lastColumn = i;
Logger.log("Last column with data = " + lastColumn); //DEBUG
var setrgh = mainSheet
// numColumns neds to be reviewed
var dataRange = mainSheet.getRange(startRow, 1, Rowlast + 1, 120); //rowStart, columnStart, row count, column count, the column count needs to be large enough to encompass all your ancillary data
// start row = 5, 1= column A, 4, rows, 120, columns
Logger.log("startRow = " + startRow + ", and the datarange = " + dataRange.getA1Notation()); //DEBUG
var data = dataRange.getValues();
Logger.log("length of data =" + data.length); //DEBUG
var lastid = 0;
for (i = 0; i < data.length; i++) {
if (i == 0) {
// if this is the first row, then assign anything but zero to last id
lastid = 100;
Logger.log(" this is the first row; set last id to 100");
}
var thisid = data[i][0];
// evaluate whether this is a duplicate ID
if (thisid == lastid) {
// this is a dup
Logger.log("i=" + i + ". This is a dup" + ", name is " + data[i][2]); //DEBUG
var stufftocopyrange = mainSheet.getRange(startRow + i, 3, 1, 3);
var stufftocopy = stufftocopyrange.getValues();
Logger.log("the range to copy is " + stufftocopyrange.getA1Notation()); //DEBUG
var targetrange = mainSheet.getRange(startRow + lastid - 1, lastColumn + 1, 1, 3);
targetrange.setValues(stufftocopy);
lastColumn = lastColumn + 3;
var duprange = mainSheet.getRange(startRow + i, 1, 1, 5);
Logger.log("the range to clear is " + duprange.getA1Notation()); //DEBUG
duprange.clearContent();
} else {
// no dup
//assign lastid value
var lastid = thisid;
Logger.log("i=" + i + ". No dup. Last id set to " + lastid); // DEBUG
} // if
} // end for loop
}
BEFORE
AFTER
The solutions previously posted didn't quite get the result I needed, however I managed to cobble together something that works for my purposes. It expects to see data in the format like:
And turn it into something like this:
Where it uses duplicate ID numbers (with an indeterminite number of duplicates) to pull only certain columns of data from the duplicate lines and append it to the first line.
function stripMiner() {
var ss = SpreadsheetApp.openById("1WDPoTICQvdruxfhAwHLtA51fz05DqyZ-NhNfpAyPO6Y");
var mainSheet = ss.getSheetByName("Main");
var startRow = 5;
var numRows = mainSheet.getLastRow();//obtains the last row in the sheet
var setrgh = mainSheet
var dataRange = mainSheet.getRange(startRow, 1,3,120); //rowStart, columnStart, row count, column count, the columncount needs to be large enough to encompass all your ancillary data
var data = dataRange.getValues();
var iter = 0;
var maxItRow = 4;
var prevIdNum = 0;
var dupCount = 1;
var cc1 = "P5"; //Cells to dump check values into
var cc2 = "P6";
var dumpRow = startRow;
//if (numRows >= maxItRow){var maxIter = maxItRow;}
for (i in data){
if (iter != maxItRow){ //making sure we haven't gone over the iteration limit
var row = data[i];
var idNum = (row[0]);
var jCount = 0; //resets icount if the id number is different icount is used to skip some cells in a row
if (idNum == prevIdNum){//only proceed if we've hit another line with the same ID number
dupCount = +1; //increment the dupcount value
mainSheet.getRange(cc2).setValue("dupCount"+dupCount); //dupcount check value
var rowIterStart = 5; //RowIterStart is used to add to rowiter, EG if your data is 20 columns wide, and you start transposing from column 4, then this will want to be about 17
var rowIter = 1;
for (j in row){
if (jCount >= 2){ //the integer here is the column where it will begin to transpose data
mainSheet.getRange(dumpRow-2,(rowIterStart*dupCount)+(rowIter)).setValue(row[j]); //startRow+(iter-dupCount)
mainSheet.getRange(cc1).setValue("dumprow"+dumpRow);
}
rowIter+=1;
jCount +=1;
}
}
else{
var dupCount = 1;
dumpRow +=1;
}
prevIdNum = (row[0]); //sets the most recently processed rows ID number
}
iter +=1;
}
}
Hopefully someone else who wants to do a similar thing can make use of this too.

Javascript loop not appending value in each container

This might be such a simple fix, but I think I've been stuck on this issue for way to long. Can someone spare a pair of eyes and see where I'm messing up?
I'm pulling data from a json file and trying to append the right value with the right container. Right now it's placing all the values in the first div container.
var metaDataArray = resultSet.searchResult[i].metadatatoshow;
for (var xx = 0; xx < metaDataArray.length; xx++) {
var metaDataContainer = "<div id='itemMetaDataContainer_"+xx+"'>Meta Data:" + metaDataArray[xx].name + "-</div>"
for (var xxx = 0; xxx < metaDataArray[xx].meta_values.length; xxx++) {
$('#itemMetaDataContainer_'+xx).append(metaDataArray[xx].meta_values[xxx]);
}
itemDetailsDiv.append(metaDataContainer);
}
you are trying to select a container before you append it to the details div. try appending the data values array to your container the following way:
var metaDataArray = resultSet.searchResult[i].metadatatoshow;
for (var xx = 0; xx < metaDataArray.length; xx++) {
var metaDataContainer = $("<div id='itemMetaDataContainer_" + xx + "'>Meta Data:" + metaDataArray[xx].name + "-</div>");
for (var xxx = 0; xxx < metaDataArray[xx].meta_values.length; xxx++) {
metaDataContainer.append(metaDataArray[xx].meta_values[xxx]);
}
itemDetailsDiv.append(metaDataContainer);
}
UPDATE:
a jQuery object holds the plain html element in index [0] (assuming its not an array).
to get the html out of metaDataContainer you can find it in metaDataContainer[0]
therefore, in order to append the jQuery object metaDataContainer to the plain html element itemDetailsDiv, you need to do the following:
itemDetailsDiv.innerHtml + = metaDataContainer[0];
UPDATE 2:
Here is a plain JavaScript solution:
var metaDataArray = resultSet.searchResult[i].metadatatoshow;
for (var xx = 0; xx < metaDataArray.length; xx++) {
//initializing the html string with the container opening tag and the description:
var metaDataContainer = "<div id='itemMetaDataContainer_" + xx + "'>Meta Data:" + metaDataArray[xx].name;
//looping through the values and adding them to the html string:
for (var xxx = 0; xxx < metaDataArray[xx].meta_values.length; xxx++) {
//create a span for each value and add it to the html string
metaDataContainer+="<span>" + metaDataArray[xx].meta_values[xxx] + "</span>";
//add a line break just for aesthetics, you can change to suit your formatting requirements
metaDataContainer+="<br/>";
}
//finish by adding a closing tag for the container
metaDataContainer+="-</div>"
//append the new html string to the details div
itemDetailsDiv.innerHtml +=metaDataContainer;
}

Return values from rows in a 2d array with a specific value - list results in HTML inside Google Script

I am trying to list three values from each row that contains a specific value in the "Status" column within coded HTML inside a Google Script function. When I run the sendDailyDigest function below, it times out. I am assuming a have some type of error in the for loop inside the html message variable, but I can't seem to figure it out.
I am relatively new to scripting and would be grateful for someone pointing me in the right direction.
Thank you!
function sendDailyDigest() {
var ss = SpreadsheetApp.openById(PRIMARY_SPREADSHEET_ID);
var sheet = ss.getSheets()[0];
var lastRow = sheet.getLastRow();
var data = getRowsData(sheet);
// Count how many requests are awaiting approval
var numSubmitted = 0;
for (var i = 2; i < lastRow; i++) {
if (sheet.getRange(i, getColIndexByName("Status")).getValue() == "SUBMITTED") {
numSubmitted++;
}
}
var message = "<HTML><BODY>"
+ "<P>" + "The following requests await your approval."
+ "<P>" + "\xa0"
+ "<P>" + "<table><tr><td><b>Request ID</b></td><td><b>Requested By</b></td><td><b>Start Date</b></td></tr>"
// List each request pending approval
for (var j = 0; j < data.length; ++j) {
var row = data[j];
row.rowNumber = j + 2;
if (row.status == "SUBMITTED") {
"<tr><td>" + row.rowNumber + "</td><td>" + row.username + "</td><td>" + row.firstDay + "</td></tr>"
}
}
+ "</table>"
+ "</HTML></BODY>";
GmailApp.sendEmail('username#domain.com', numSubmitted + 'Leave Requests Awaiting Approval', '', {htmlBody: message});
}
function getColIndexByName(colName) {
var ss = SpreadsheetApp.openById(PRIMARY_SPREADSHEET_ID);
var sheet = ss.getSheets()[0];
var numColumns = sheet.getLastColumn();
var row = sheet.getRange(1, 1, 1, numColumns).getValues();
for (i in row[0]) {
var name = row[0][i];
if (name == colName) {
return parseInt(i) + 1;
}
}
return -1;
}
/////////////////////////////////////////////////////////////////////////////////
// Code reused from Reading Spreadsheet Data using JavaScript Objects tutorial //
/////////////////////////////////////////////////////////////////////////////////
// getRowsData iterates row by row in the input range and returns an array of objects.
// Each object contains all the data for a given row, indexed by its normalized column name.
// Arguments:
// - sheet: the sheet object that contains the data to be processed
// - range: the exact range of cells where the data is stored
// This argument is optional and it defaults to all the cells except those in the first row
// or all the cells below columnHeadersRowIndex (if defined).
// - columnHeadersRowIndex: specifies the row number where the column names are stored.
// This argument is optional and it defaults to the row immediately above range;
// Returns an Array of objects.
function getRowsData(sheet, range, columnHeadersRowIndex) {
// etc.
The most obvious error I see right now is that you are using the .getLastRow() method, which always times out, even if your code is functional. Instead, try using .getMaxRows(). I used to use the .getLastRow() method at first, but then realized that it doesn't work for some reason. When I used the .getMaxRows() method, the for loop did not time out on me.

Google Apps Script: How to fix this for loop to insert paragraphs of one google document to another?

So I'm arriving at last stops of my Apps journey (there'll be several others =).
The code bellow is a function that aims to do this: iterates through the paragraphs of one google text document and, when it finds in text some sinal, some paragraph (such as "Introduction", "Part 1 - Background" or "Part 2 - Biography") whose content is igual a theses_type, it inserts all theses_type paragraphs into the first document, after that sinal or theses_type keyword.
So, I'm trying to do this with the function "importTheses" (thanks #Serge insas and others for previous help!). But I'm having trouble after the line for( var k = 0; k < thesesParagraphs-1; ++k ). Even when I got through the log the boolean True, I can't get the paragraphs inserted. I also can't get the log of this line: Logger.log("thesesDoc.getText() = " + thesesElement.getText() );. So, any help or hint will be very appreciated.
function importTheses(targetDocId, thesesId, thesesType) { // just a name, I used it to analyse docs
var targetDoc = DocumentApp.openById(targetDocId);
var targetDocParagraphs = targetDoc.getParagraphs();
var targetDocElements = targetDoc.getNumChildren();
var thesesDoc = DocumentApp.openById(thesesId);
var thesesParagraphs = thesesDoc.getParagraphs();
var thesesElements = thesesDoc.getNumChildren();
Logger.log("targetDocId = " + targetDocId);
Logger.log("thesesId = " + thesesId);
Logger.log("thesesType = " + thesesType);
var elTargetDoc=[];
var elTheses=[];
for (var j = 0; j < targetDocElements; ++j ) {
var targetDocElement = targetDoc.getChild(j);
Logger.log("targetDoc.getChild(j) = " + targetDocElement);// to see targetDoc's content
elTargetDoc[j]=targetDocElement.getText();
if(elTargetDoc[j] == thesesType){
Logger.log("elTargetDoc[j]== " + elTargetDoc[j]);
Logger.log("thesesType " + thesesType);
Logger.log("if(elTargetDoc[j]== thesesType)" + (elTargetDoc[j]== thesesType) );
for( var k = 0; k < thesesParagraphs-1; ++k ) {
var thesesElement = thesesDoc.getChild(k);
Logger.log("thesesDoc.getChild(k) " + thesesDoc.getChild(k));
Logger.log("thesesDoc.getText() = " + thesesElement.getText() );
elTheses[k] = thesesElement.getText();
targetDoc.insertParagraph(j, elTheses[k]);
}
}
}
}
for( var k = 0; k < thesesParagraphs-1; ++k ) { .. }
In this line of code, you're intention is to loop using k, over integer values starting at 0. The loop should run at least once, as long as thesesParagraphs is 2 or more... and is a number.
That second condition is your problem. Earlier in the function, you had this:
var thesesParagraphs = thesesDoc.getParagraphs();
... so thesesParagraphs is an Array of Paragraph objects, not a number. You are probably interested in the count of paragraphs:
for( var k = 0; k < thesesParagraphs.length-1; ++k ) { .. }
^^^^^^^
Or perhaps thesesElements was what you intended to use to bound your loop, since it's a number.

Categories

Resources