Google Sheets Script - Loop values in different cells - javascript

I come from VBA and Python so still trying to get the hang of javascript in Google Sheets. I am trying to do something pretty simple.
Find last row in column D - Sucessful
Name array in Column D from row 2 thru LastRow - Sucessful
Copy value of cell for row i and paste in column E - Not Successful
I need it to look in data[0][0] and copy the info in column E, then look in data[1][0] and copy info in column E, etc., etc. up to the lastRow. Everything works up till the last line and I get an error saying the "The coordinates or dimensions of the range are invalid."
Here is my code: (FYI: lastrow() is a function that finds last row # in column specified)
function GatherStoreInfo() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var ReportSheet = ss.getSheetByName('Report');
for (var i = 0; i < lastRow('A') + 1; i = i+1) {
var data = ReportSheet.getRange(2, 4, lastRow('D'), 1).getValues();
ReportSheet.getRange(i, 2).setValue(data[i][0]); // <- This doesn't work
}
}

Ok after messing around with it more I figured it out. I guess I'll post the answer just in case someone stumbles on this.
Basically it was because I was starting i at 0 and then in the range I was telling it to start at row 0 which doesnt exist, so I had to add i + 1 in the range and it worked. Here's the final code:
function GatherStoreInfo() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var ReportSheet = ss.getSheetByName('Report');
for (var i = 0; i < lastRow('A'); i = i+1) {
var data = ReportSheet.getRange(1, 4, lastRow('D'), 1).getValues();
SpreadsheetApp.getActiveSheet().getRange(i + 1, 5).setValue(data[i]);
}
}

Related

Calendar import - new events inserted by date messes up other columns of data

This Code is the only one that works to input my shared google calendar into a sheet. I use a master Calendar for all appointments in my medical office. Calendar data comes in 4 columns Columns B, C, D, E (from the calendar (start time, location, description, Title). I have added Columns F - O with additional data such as 'no show', time of arrival, rescheduled for another day and formulas that calculate the number of days between appointments.
When the calendar events update from the calendar to the sheet, my added columns of F-O no longer match the row of data. For example, my entered data of 'no show' for that appointment will be in the cell above where it should be. How do I fix this so that new rows of data are added to the bottom of the sheet and stay there so that I don't have mixed up data?
function importCalendar(){
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Calendar Import');
var calendarName = sheet.getRange('C2').getValue();
var start = sheet.getRange('C3').getValue();
var end = sheet.getRange('C4').getValue();
var calendar = CalendarApp.getCalendarById(calendarName);
if(calendar) {var calendarId = calendar.getId();}
if(!calendar) {
var calendar = CalendarApp.getCalendarsByName(calendarName)[0];
var calendarId = calendarName;
}
var events = calendar.getEvents(start, end);
var eventDetails = [];
for(var i = 0; i<events.length; i++){
eventDetails.push([events[i].getStartTime(), events[i].getTitle(), events[i].getDescription(), events[i].getLocation()]);
}
//write calendar details to spreadsheet
var startRow = 8;
var startCol = 2;
for(var j = 0; j<eventDetails.length; j++){
var tempRange = sheet.getRange(startRow+j, startCol, 1, 4);
var eventArray = new Array(eventDetails[j]);
tempRange.setValues(eventArray);
}
return eventDetails;
}
I believe your question is just, "How do I fix this so that new rows of data are added to the bottom of the sheet...?"
Right now, you've defined startRow = 8, meaning that you're always going to start printing from that row and overwriting whatever is there. Instead, you could try using the getLastRow() method, which returns the position of the last row that has content.
var startRow = sheet.getLastRow() + 1; // Start one after the last row
There is a best practice to use batch operations. It's not always appropriate, but I think it may be in your case. If you had 5 events to print, right now you'd call getRange() and setValues() 5 times, but you can very easily update your code to call those only once for printing.
Alternatively, you could use appendRow(), but since you're not printing in column A and it's not a batch operation, I don't think it would be the best approach.

Move Row to Other Sheet Based on Cell Value

I have a sheet with rows I'd like to move to another sheet based on a cell value. I tried following this post's solution (refer below), but I'm having trouble editing the script towards what I want it to do.
I'm doing Check-Ins for an event. I would like to be able to change the value in Column F, populate Column G with the time the status changed, and for the row data to migrate to the Attendee Arrived sheet.
I believe the script already does this, but one has to run it manually. It also takes care of deleting the row data in A (Event) after migrating it to B (Attendee Arrived).
My question is could someone please help me set it up in order for script to run continuously (on edit), and also accomplish all of the above if I missed something?
I don't believe the script will respect the drop down format as it runs so I'm willing to manually type in something. It'd be cool if it could stay that way though - makes it easier for one.
Here's the sheet I'm testing on.
https://docs.google.com/spreadsheets/d/1HrFnV2gFKj1vkw_UpJN4tstHVPK6Y8XhHCIyna9TLJg/edit#gid=1517587380
Here's the solution I tried following. All credit to Jason P and Ritz for this.
Google App Script - Google Spreadsheets Move Row based on cell value efficiently
Thank you D:
function CheckIn() {
// How Many Columns over to copy
var columsCopyCount = 7; // A=1 B=2 C=3 ....
// What Column to Monitor
var columnsToMonitor = 6; // A=1 B=2 C=3 ....
//TARGET SPREAD SHEETS
var target1 = "Attendee Arrived";
//Target Value
var cellvalue = "Attendee Arrived";
//SOURCE SPREAD SHEET
var ss = SpreadsheetApp.openById('1HrFnV2gFKj1vkw_UpJN4tstHVPK6Y8XhHCIyna9TLJg');
var sourceSpreadSheetSheetID = ss.getSheetByName("Event");
var sourceSpreadSheetSheetID1 = ss.getSheetByName(target1);
var data = sourceSpreadSheetSheetID.getRange(2, 1, sourceSpreadSheetSheetID.getLastRow() - 1, sourceSpreadSheetSheetID.getLastColumn()).getValues();
var attendee = [];
for (var i = 0; i < data.length; i++) {
var rValue = data[i][6];
if (rValue == cellvalue) {
attendee.push(data[i]);
} else { //Fail Safe
attendee.push(data[i]);
}
}
if(attendee.length > 0){
sourceSpreadSheetSheetID1.getRange(sourceSpreadSheetSheetID1.getLastRow() + 1,
1, attendee.length, attendee[0].length).setValues(attendee);
}
//Will delete the rows of importdata once the data is copided to other
sheets
sourceSpreadSheetSheetID.deleteRows(2,
sourceSpreadSheetSheetID.getLastRow() - 1);
}
Try this:
I imagine that you already know that you'll need an installable onEdit Trigger and that you can't test a function of this nature by running it without the event object.
function checkIn(e) {
var sh=e.range.getSheet();
if(sh.getName()!="Event") return;
if(e.range.columnStart==6) {
if(e.value=="Attendee Arrived"){
e.range.offset(0,1).setValue(Utilities.formatDate(new Date(), Session.getScriptTimeZone(), "M/d/yyyy HH:mm:ss"));
var row=sh.getRange(e.range.rowStart,1,1,sh.getLastColumn()).getValues()[0];
e.source.getSheetByName("Attendee Arrived").appendRow(row);
sh.deleteRow(e.range.rowStart);
}
}
}

Google Sheets Script - Search in Arrays and Replace Values in Next Column

For some reason I'm not really able to perform a trivial search and replace in google script (using google sheets).
What I want to do is
Read values form an array in Sheet1
Find the value in a 2D-array in sheet2 (two columns). When the value is found, the script adds '+1' to the value in the column next to the found value in sheet 2.
Repeat for the next value in sheet1
Assume I have a value "Muse" in Sheet 1. I want to search for "Muse" in sheet two and if the column next to this value has already a 1, it should add one and write it back. That's it. Then do the same with the next value in sheet1.
I searched for similar scripts but was unfortunately not able to adapt something to my needs.
Edit: It's driving me nuts, this is what I have at the moment:
function findingReplacing()
{
var sh1 = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('sheet1');
var sh2 = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('sheet2');
var searchfor = sh1.getRange('B2').getValues();
var replacewith = sh2.getDataRange().getValues();
for(var i=1;i<replacewith.length;i++)
{
if(replacewith[i][0]==searchfor)
{
replacewith[i][1]=1;
}
}
//sheet2.SetValues(replacewith);
sh2.setValues(replacewith);
}
just answered it myself: this works:
function findingReplacing()
{
var sh1 = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('sheet1');
var sh2 = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('sheet2');
var CustDunned = sh1.getRange('B2').getValues();
var range = sh2.getRange("A:B");
var rangeA = range.getValues()
for(var i=1;i<rangeA.length;i++)
{
if(rangeA[i][0]==CustDunned)
{
rangeA[i][1]=rangeA[i][1]+1;
}
}
range.setValues(rangeA);
}

Google app script - looping through the rows in a spreadsheet

I am trying to loop through rows within a spreadsheet and identify if a particular row has the key word "hello" and move that entire row into a new spreadsheet.
I have attempted the following code. The code works for the first row but doesn't loop through and stops after the first row. Expanding the range selection to "C1:E32" does not help.
function Edit(e) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var activatedSheetName = ss.getActiveSheet().getName();
var ActiveSheet = ss.getSheetByName("ActiveSheet"); // source sheet
var MoveDatatoThisSheet = ss.getSheetByName("MoveDatatoThisSheet"); // target sheet
var re = new RegExp(/(Hello)/i);
var startRow = 1;
var endRow = ss.getLastRow();
var getRange = ss.getDataRange();
var getRow = getRange.getRow();
for (var ree = startRow; ree <= endRow; ree++) {
// if the value in column D is "Approved", move the row to target sheet
cellValue = ss.getRange("C1:E1");
if (cellValue.getValue().match(re)) {
// insert a new row at the second row of the target sheet
MoveDatatoThisSheet.insertRows(2, 1);
// move the entire source row to the second row of target sheet
var rangeToMove = ActiveSheet.getRange(/*startRow*/ getRow, /*startColumn*/ 1, /*numRows*/ 1, /*numColumns*/ ActiveSheet.getMaxColumns());
rangeToMove.moveTo(MoveDatatoThisSheet.getRange("A2"));
// add date and time of when approved to target row in column E
MoveDatatoThisSheet.getRange("E2").setValue(Date());
// delete row from source sheet
ActiveSheet.deleteRow(cellValue, 1);
}
}
}
Your loop never uses the variable ree, it only operates with cellValue = ss.getRange("C1:E1").
Another problem is that deletion shifts the rows under the deleted one, possibly causing subsequent operations to act on a wrong row. When you go through an array of rows, deleting some of them, do it bottom up, not top down.
for (var ree = endRow; ree >= startRow; ree--) {
var rangeToCheck = ss.getRange(ree, 3, 1, 3); // 3 columns starting with column 3, so C-E range
if (rangeToCheck.getValues()[0].join().match(re)) { // joining values before checking the expression
MoveDatatoThisSheet.insertRows(2,1);
var rangeToMove = ActiveSheet.getRange(/*startRow*/ getRow, /*startColumn*/ 1, /*numRows*/ 1, /*numColumns*/ ActiveSheet.getMaxColumns());
rangeToMove.moveTo(MoveDatatoThisSheet.getRange("A2"));
// add date and time of when approved to target row in column E
MoveDatatoThisSheet.getRange("E2").setValue(Date());
// delete row from source sheet
ActiveSheet.deleteRow(ree);
}
}
If the goal is to check only column D (say), the code simplifies slightly
var rangeToCheck = ss.getRange(ree, 4); // column D in row ree
if (rangeToCheck.getValue().match(re)) { // joining values before checking the expression
Performance
As Google recommends, one should avoid multiple calls to getValues / setValues and such, instead grabbing all necessary data at once, processing it, and making batch changes at once. E.g., instead of placing it a row in another sheet, add it to an array; when the loop ends, place the entire array in that sheet.

How to make [Conditional Formatting] script?

I want,
If Sheet1 ColumnB = Sheet89 ColumnA
Then matched Sheet1 Column B cells will be green
Here is my demo sheet.
Based on some guideline I made this but not working.
function formatting() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Sheet1');
var columnB = sheet.getRange(1, 2, sheet.getLastRow()-1, 1);
var bValues = columnB.getValues();
var sheet89 = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Sheet89');
var columnO = sheet89.getRange(1, 1, sheet.getLastRow()-1, 1);
var oValues = columnO.getValues();
for (var h = 0; h < bValues.length; h++) {
for (var i = 0; i < oValues.length; i++) {
if (oValues[i][0] == bValues[h][0]) {
sheet.getRange(i + 2, 1, 1, 1).setBackgroundColor('green');
}
}
}
}
This solution below will iterate through each cell with a value in column B of sheet1 and check it against every value in Column A of sheet89 (although you named this ColumnO, according to your getValues function, it will grab values from Column A).
If it finds a match, it will turn green the cell in column B of sheet1. In your example code you use the i loop variable (which iterates through rows on sheet89) to get the cell on sheet1 to turn green. It's not clear which cells you want to turn green. I assumed it was the cells on sheet1 so I changed the the code to
sheet.getRange(h+1, 2).setBackgroundColor('green');
Also, the getRange function for a single cell only requires 2 arguments, so I removed the numRows and numColumns arguments for the line which colors the cell green.
I'm not sure why bValues and oValues exclude the last row, but I removed the -1 in each of these as it will cause the code to fail if for any reason it is run on a blank worksheet. The getLastRow() returns the last row with a value, not the next blank row in the sheet. If you want to capture the whole sheet, then you shouldn't use -1.
function formatting() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Sheet1');
var columnB = sheet.getRange(1, 2, sheet.getLastRow(), 1);
var bValues = columnB.getValues();
var sheet89 = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Sheet89');
var columnO = sheet89.getRange(1, 1, sheet.getLastRow(), 1);
var oValues = columnO.getValues();
for (var h = 0; h < bValues.length; h++) {
for (var i = 0; i < oValues.length; i++) {
if (oValues[i][0] == bValues[h][0]) {
sheet.getRange(h + 1, 2).setBackgroundColor('green');
}
}
}
}
What I understand to be required (and not what the example sheet shows at present) is possible with Conditional formatting.
In Google Spreadsheets conditional formatting across sheets is not nearly as straightforward as within a single sheet, because of security and therefore authorisation. You may, for speed for example, prefer to copy the content of Sheet89 into Sheet1 (just two cells) to avoid that issue, or indeed write a script instead. At least keep the ranges as small as practical.
However it is possible, though may be slow and require authorisation.
Please clear any conditional formatting from Sheet1 ColumnA then:
Select ColumnA in Sheet1, Format, Conditional formatting..., Format cells if... Custom formula is and
=countif(IMPORTRANGE(" k e y ","Sheet89!A:A"),A1)<>0
with highlighting of your choice and Done.
k e y above represents the unique identification code for Sheet89 (will look something like 1u4vq8vDne-aKMVdJQPREGOxx7n99FqIb_kuJ_bG-PzM).
The image shows at present what is in ColumnC of the image (but is in ColumnA of the example) and F1 and F2 in the image show what is in ColumnA of Sheet89 of the example. The paler brown has been applied with Conditional formatting::

Categories

Resources