comparing json array rows with value on destination rows (apps script) - arrays

I've use this script before, and work well, here's script, This script copies the values and paste to last row in google sheet,
function doPost(request = {}) {
const { parameter, postData: { contents, type } = {} } = request; //request data
const { dataReq = {} } = JSON.parse(contents); //content
const { fname = {} } = JSON.parse(contents); //function name
const response = {
status: "function not found: " + fname, // prepare response in function not found
data2: dataReq
}
switch (fname) { //function selection
case 'pasteData':
var output = JSON.stringify(pasteDAta(dataReq)) //call function with data from request
break
default:
var output = JSON.stringify(response)
break
}
return ContentService.createTextOutput(output).setMimeType(ContentService.MimeType.JSON); //response to frontend
}
function pasteDAta(dataReq) {
const id = '1_27rjNQmlXrwVKpLWUbGrJYPJufGRa7Dk-XEKcNAHr0'; //id of Google Sheet
var sheet = SpreadsheetApp.openById(id).getSheetByName('Sheet1'); //sheet
var headings = sheet.getDataRange().getValues()[0]; //Headers
var values = dataReq.map((a) => {
let holder = [];
for (x in headings) {
let output = (headings[x] in a) ? a[headings[x]] : '';
holder.push(output);
}
return holder;
});
var len = values.length;
sheet.getRange(sheet.getLastRow() + 1, 1, len, values[0].length).setValues(values);
return "Numbers of sheets added: " + len;
}
I want this script to be able to check the row values ​​in column b (source [json]), if the value in the source rows in column b is the same as the value in the destination rows in column b (google sheet) then the values ​​are replaced all but if not, the values ​​are copied to the last rows. If it is possible, can anyone give me a modified working script?
Example
First condition; Before (Destination Sheet)
Date
Code
Name
Grade
02/04/21
Math1
John
80
02/04/21
Math2
John
80
Expected results
After replacing (from JSON)- if Column B (Code) is the same as the source
Date
Code
Name
Grade
02/04/21
Math1
Dare
78
02/04/21
Math2
Brian
90
Second condition; Before (Destination Sheet)
Date
Code
Name
Grade
02/04/21
Bio1
Anton
78
02/04/21
Bio2
Julian
65
Expected results
After after appending to last row (from JSON)- if Column B isn't same as the source
Date
Code
Name
Grade
02/04/21
Bio1
Anton
78
02/04/21
Bio2
Julian
65
02/04/21
Math1
Dare
78
02/04/21
Math2
Brian
90

Try to change the function pasteDAta() this way:
function pasteDAta(dataReq) {
const id = "1_27rjNQmlXrwVKpLWUbGrJYPJufGRa7Dk-XEKcNAHr0";
var sheet = SpreadsheetApp.openById(id).getSheetByName("Sheet1");
// get header and the rest data from the sheet
var [ headings, ...data ] = sheet.getDataRange().getValues();
var values = dataReq.map((a) => {
let holder = [];
for (x in headings) {
let output = headings[x] in a ? a[headings[x]] : "";
holder.push(output);
}
return holder;
});
var len = values.length;
var new_values = []; // values to add at the bottom of the sheet
var col_b = data.map(x => x[1]); // get column B from the data
values.forEach(row => {
// find an index of the row with the same value in cell B
var row_index = col_b.indexOf(row[1]);
// if nothing was found add the row to the new values
if (row_index == -1) new_values.push(row);
// else change the found row on the sheet
else sheet.getRange(row_index + 2, 1, 1, row.length).setValues([row]); // '+2' due the header
})
// add the new values at the bottom of the sheet
if (new_values.length > 0)
sheet.getRange(sheet.getLastRow() + 1, 1, len, new_values[0].length).setValues(new_values);
return "Numbers of sheets added: " + len;
}

Related

Apps Script Google Sheets Birthday Reminder Range in Array

I'm trying to write an automatic birthday reminder for my team.
It's supposed to check if a persons birthday is today and if so, send a mail to everyone else in the team.
In Google Sheets, the four columns are: name, surname, e-mail and birthday. First row are headers.
This is what I got so far (mostly copied):
`
function main() {
// Load the sheet that contains the birthdays.
var sheet = SpreadsheetApp.getActive().getSheetByName("Geburtstage");
// Get the last row in the sheet that has data.
var numRows = sheet.getLastRow();
// Load data in the first two columns from the second row till the last row.
// Remember: The first row has column headers so we don’t want to load it.
var range = sheet.getRange(2, 1, numRows - 1, 4).getValues();
// Use a for loop to process each row of data
for(var index in range) {
// For each row, get the person’s name and their birthday
var row = range[index];
var name = row[0];
var birthday = row[3];
// Check if the person’s birthday is today
if(isBirthdayToday(birthday)) {
//If yes, send an email reminder
emailReminder(name);
}
}
}
// Check if a person’s birthday is today
function isBirthdayToday(birthday) {
var today = new Date();
if((today.getDate() === birthday.getDate()) &&
(today.getMonth() === birthday.getMonth())) {
return true;
} else {
return false;
}
}
// Function to send the email reminder
function emailReminder(name) {
var subject = "Geburtstagerinnerung: " + name;
var recipient = Session.getActiveUser().getEmail();
var body = name + " hat heute Geburtstag!";
MailApp.sendEmail(recipient, subject, body);
}
`
All that is missing is to replace the recipient by all e-mails in the third column, except for the person whos birthday it is.
My idea is, to save the range of the third column (e-mails) to an array, drop the e-mail whos birthday it is, and pass it to recipient as a comma separated string.
Afterwards reset the array in case two people have the same birthday.
My problem is, that I have no idea what I'm doing and all the solutions I found are overly complicated.
Try this:
function main() {
const dt = new Date();
const dtv = new Date(dt.getFullYear(),dt.getMonth(),dt.getDate()).valueOf();
const ss = SpreadsheetApp.getActive();
const sh = ss.getSheetByName("Sheet0");
const vs = sh.getRange(2, 1, sh.getLastRow() - 1, 4).getValues();
let recipients;
vs.forEach((R,i) => {
let d = new Date(R[3]);
let dv = new Date(d.getFullYear(),d.getMonth(),d.getDate()).valueOf();
if(dv == dtv) {
recipients = vs.filter((r,j) => j != i ).map( r => r[2]).flat().join(",")
GmailApp.sendEmail(recipients,`Todays ${R[0]} Birthday`,"");
}
});
}

Searching for a value and if a value exists adding a row with copying the data in google spreadsheet

I want to search for a word in a certain column and then if the value exists, I want to copy the row below with its values and change the word to two different words.
My issue was in getting the found word row Number to insert a row below it.
function myFunction() {
var ss = SpreadsheetApp.openById("1fE404JUbw3aytlqtoht6FfrIhYhTpSe2MM5UDnBkFc4");
var sheet = ss.getSheets()[0]
let range = sheet.getDataRange();
var values = range.getValues();
var typeRange = sheet.getRange("E2:E");
var typeValues = typeRange.getValues();
var i;
for(i = 0; i <= lastRow ; i++){
for (let type of typeValues){
if (type == "Both"){
var bothRow = i+1;
}
//// var bothRow = sheet.getRange(i+1,1,1,typeValues[i].length);
//// ss.insertRowsAfter(bothRow, 1);
}
}
}
I have used alert to check and it inserted an infinite number of rows after row number 1.
function myFunction() {
let sheetui= SpreadsheetApp.getUi()
sheetui.createMenu("Rahaf Game")
.addItem("Stage 1", 'pop')
.addToUi();
// var ss = SpreadsheetApp.openById("1fE404JUbw3aytlqtoht6FfrIhYhTpSe2MM5UDnBkFc4");
// var sheet = ss.getSheets()[0]
// let range = sheet.getDataRange();
// var values = range.getValues();
// var typeRange = sheet.getRange("E2:E");
// var typeValues = typeRange.getValues();
}
function pop(){
//var ss = SpreadsheetApp.openById("1fE404JUbw3aytlqtoht6FfrIhYhTpSe2MM5UDnBkFc4");
var sheet = ss.getSheets()[0]
var typeRange = sheet.getRange("E2:E");
var typeValues = typeRange.getValues();
var lastRow = sheet.getLastRow();
var i;
for(i = 0; i <= lastRow ; i++){
for (let type of typeValues){
if (type == "Both"){
var bothRow = i+1;
ss.insertRowsAfter(bothRow, 1);
}
//// var bothRow = sheet.getRange(i+1,1,1,typeValues[i].length);
//// ss.insertRowsAfter(bothRow, 1);
}
}
}
Can someone please help in achieving the required result in inserting a row below and copy the values into it with changing the word into two words?
Answer:
.getValues() returns a 2D array of values and you are referencing it as if it is unidimentional.
Code fix:
You need to change your conditional such that the object you are checking is an array:
if (type == "Both"){
//code
}
Should be:
if (type == ["Both"]){
//code
}
Adding a row and copying the data to it:
You can add a row in a sheet after a given row with the insertRowsAfter() and use .getRange().setValues() to copy in the data:
for(var i = 0; i <= lastRow ; i++){
for (let type of typeValues){
if (type == ["Both"]){
var bothRow = i + 1;
ss.insertRowsAfter(bothRow, 1);
// increment lastRow as there is now one more row in the sheet
lastRow++;
// increment i as you want to skip the row you just copied!
i++;
var rangeToCopy = sheet.getRange(bothRow + ':' + bothRow).getValues();
sheet.getRange((bothRow + 1) + ':' + (bothRow + 1)).setValues(rangeToCopy);
}
}
}
Don't forget to increment lastRow by 1 if you add a new row so that your loop still reaches the end of the sheet.
References:
Class Range | Apps Script - Method getValues()
Related Questions:
Using Google Sheets scripts, why does my if statement always return false when comparing cell values?

loop through array, find sub-string in cell, replace text in Google Sheets

I want to create a function in Google Sheets that loops through an array, finds a string, and replaces the text of the string with a new value. I have code that works but it only replaces the text if the cell ONLY contains the string (ie search for "office" replace with "Office" only works if the cell only contains "office). I want to be able to replace "office" with "Office" even if the cell contains "office 867". Below is the code that works. Also, how can I get the function to search column A, row 1 to whatever? As of now, the function only finds and replaces if the values are in B2 to whatever (ie the first column A is not searched and the first row of every other row is not searched. Function works on B2 to whatever but won't work on B1, C1, D1, etc. I know it is because of the searchRange variable but I can't figure out how to set the parameters so that it searches the entire sheet (ie all columns and all rows). So here's the code I have so far. The questions I have are in the code as comments:
// This code works but ONLY if entire cell matches the string being searched for
// I want to search for all cells that contain the string even if other text is present
// So if cell contains "office 98" I want to change that to "Office 98"
// If cell contains "blg 78" I want to change that to "Building 78"
// If cell contains "space 9876" I want to change that to "Space 9876"
// Also, how do I change the var searchRange so that the range is the entire sheet?
var ui = SpreadsheetApp.getUi();
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var rangeData = sheet.getDataRange();
var lastColumn = rangeData.getLastColumn();
var lastRow = rangeData.getLastRow();
var searchRange = sheet.getRange(2,2, lastRow-1, lastColumn-1);
function findReplace() {
var rangeValues = searchRange.getValues();
for ( i = 0; i < lastColumn - 1; i++){
for ( j = 0 ; j < lastRow - 1; j++){
if(rangeValues[j][i] === "office"){
sheet.getRange(j+2,i+2).setValue("Office");
}else if (rangeValues[j][i] === "blg"){
sheet.getRange(j+2,i+2).setValue("Building");
}else if (rangeValues[j][i] === "space"){
sheet.getRange(j+2,i+2).setValue("Space");
};
};
};
};
You can use getDataRange function [1] to get the entire range where there's data in the sheet. To avoid matching the entire string, use indexOf method [2] to check if the searched string is inside the cell string. To only replace the searched string, use replace function [3] on the cell value. I also put a string validation so any other type of cell value will be ignored (Number, Date, Boolean):
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var searchRange = sheet.getDataRange();
var lastColumn = searchRange.getLastColumn();
var lastRow = searchRange.getLastRow();
function findReplace() {
var rangeValues = searchRange.getValues();
for ( i = 0; i < lastColumn; i++){
for ( j = 0 ; j < lastRow; j++){
var value = rangeValues[j][i];
if(typeof value != "string") { continue; }
if(value.indexOf("office") > -1){
var newValue = value.replace("office", "Office")
sheet.getRange(j+1,i+1).setValue(newValue);
}
else if (value.indexOf("blg") > -1){
var newValue = value.replace("blg", "Building")
sheet.getRange(j+1,i+1).setValue(newValue);
}
else if (value.indexOf("space") > -1){
var newValue = value.replace("space", "Space")
sheet.getRange(j+1,i+1).setValue(newValue);
};
};
};
};
[1] https://developers.google.com/apps-script/reference/spreadsheet/sheet#getdatarange
[2] https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf
[3] https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace
function findReplace() {
const ui=SpreadsheetApp.getUi();
const ss=SpreadsheetApp.getActive();
const srchA=['office','blg','space'];
const vA=['Office','Building','Space'];
const sh=ss.getActiveSheet();
const rg=sh.getRange(2,2,sh.getLastRow()-1,sh.getLastColumn()-1);
const v=rg.getValues();
v.forEach(function(r,i){
r.forEach(function(c,j){
srchA.forEach(function(s,k){
if(v[i][j].toString().includes(s)) {
sh.getRange(i+2,j+2).setValue(vA[k]);
}
});
});
});
}
or maybe this:
function findReplace() {
const ui=SpreadsheetApp.getUi();
const ss=SpreadsheetApp.getActive();
const srchA=['office','blg','space'];
const vA=['Office','Building','Space'];
const sh=ss.getActiveSheet();
const rg=sh.getRange(2,2,sh.getLastRow()-1,sh.getLastColumn()-1);
srchA.forEach((s,k) => {
rg.createTextFinder(s).matchEntireCell(false).findAll().forEach(rg => {
rg.setValue(vA[k]);
});
}
}

Need a custom function to repeat for each row shown and minimize API calls

EDIT: I need help combining functions into 1 and adding a trigger.
In my spreadsheet I have rows 4-100 for customer service calls that are filtered from a "ServiceData" worksheet by either choosing a "Service Month" or "Service Day" (ie. "7/11" shows only 5 rows where "July" would show 65 rows) . Each row item has corresponding Place IDs for origin/destination in column K and L with an order # (as in 1st, 2nd, 3rd... service call of the day) in column J .
TravelTime spreadsheet
I'm using the following custom function travelTime() in cells M4:M100 to calculate driving duration and distance between 2 place IDs:
function travelTime(origin,destination) {
var API_KEY = PropertiesService.getScriptProperties().getProperty('keyMaps');
var baseUrl = "https://maps.googleapis.com/maps/api/distancematrix/json? units=imperial&origins=";
var queryUrl = baseUrl + "place_id:" + origin + "&destinations=" +
"place_id:" + destination + "&mode=driving" + "&key=" + API_KEY;
var response = UrlFetchApp.fetch(queryUrl);
var json = response.getContentText();
var time = JSON.parse(json);
return [[ time.rows[0].elements[0].duration.text,
time.rows[0].elements[0].distance.text ]] ;
A major issue is that many unnecessary service calls are being made to the API when I'm making edits to the "ServiceData" spreadsheet (ie. service date changes when a particular day is over-scheduled) and not needing the travel time updated until I'm done working through a schedule .
After researching quite a bit there seems to be several options I could be using; caching, looping, arrays, and putting everything into a script then attach to a button to only run when ready. Considering I'm a newbie, putting all these options together are definitely beyond my skill level and could really use some help.
EDIT with new functions:
So after more researching I have been able to put together the following functions that when each are run independently work great. Now the problem I'm having is putting these all together in particular adjusting the original travelTime()into newTravelTime(). I have made an attempt towards the right direction below but can't figure out how to get the API call in there .
function newTravelTime() {//<--**having issues how to write this function
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sourceSheet = ss.getSheetByName("MonthlySA");
var sourceR = sourceSheet.getRange(4, 11, sourceSheet.getLastRow()-3, 4);
var sourceV = sourceR.getValues();
var array = [];
for (var i = 0; i < sourceV.length; i++) {
if (sourceV[i][2] == "") {
var origin = sourceV[i][0];//ori place IDs for API query
var destination = sourceV[i][1];//des place IDs API api query
}
array.push([sourceV[i][2]]);
}
sourceSheet.getRange(4, 13, array.length, 1).setValues(array);
I'd like to create a final getTravelTime() with all the functions and add an OnEdit trigger when either "Service Month" or "Service Day" changes in cells B1 or B2 to run them. If there is any advice with my functions themselves I would really appreciate some help, I am very new with this and trying.
///checks if origin/destination are already in the cacheSheet then return travel time to sourceSheet
function getCachedTravelTime() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sourceSheet = ss.getSheetByName("MonthlySA");
var sourceR = sourceSheet.getRange(4, 11, sourceSheet.getLastRow()-3, 4);
var sourceV = sourceR.getValues();
var cacheSheet = ss.getSheetByName("TravelTimeCache");
var cacheR = cacheSheet.getRange(2, 1, cacheSheet.getLastRow()-1, 4);
var cacheV = cacheR.getValues();
var array = [];
for (var i = 0; i < sourceV.length; i++) {
for (var j = 0; j < cacheV.length; j++) {
//if origin/destination columns from sourceSheet match columns on cacheSheet
if (sourceV[i][0]+sourceV[i][1] == cacheV[j][0]+cacheV[j][1]) {
sourceV[i][2] = cacheV[j][2]; //column with travel duration
sourceV[i][3] = cacheV[j][3]; //column with travel distance
}
}
array.push([sourceV[i][2], sourceV[i][3]]);
}
sourceSheet.getRange(4, 13, array.length, 2).setValues(array);
}
///if origin or destination are blank, label as 'missing value'
function missingOD() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sourceSheet = ss.getSheetByName("MonthlySA");
var sourceID = sourceSheet.getRange(4, 3, sourceSheet.getLastRow()-3, 12);
var sourceV = sourceID.getValues();
var array = [];
for (var i = 0; i < sourceV.length; i++) {
// if ID has a value
if (sourceV[i][0] != "") {
// if origin or destination is blank
if (sourceV[i][8] == "" || sourceV[i][9] == "") {
sourceV[i][10] = 'missing value';
}
}
array.push([sourceV[i][10]]);
}
sourceSheet.getRange(4, 13, array.length, 1).setValues(array);
}
///if cache not found - get the new travelTime for that origin/destination on sourceSheet...
function newTravelTime() {//<--
}
///...and store the new travelTime() in cacheSheet
function storeTravelTime() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sourceSheet = ss.getSheetByName("MonthlySA");
var sourceR = sourceSheet.getRange(4, 11, sourceSheet.getLastRow()-3, 4);
var sourceV = sourceR.getValues();
var cacheSheet = ss.getSheetByName("TravelTimeCache");
var cacheR = cacheSheet.getRange(2, 1, cacheSheet.getLastRow()-1, 4);
var cacheV = cacheR.getValues();
var array = [];
for (var i = 0; i < sourceV.length; i++) {
var duplicate = false;
for (var j = 0; j < cacheV.length; j++) {
if (sourceV[i][0]+sourceV[i][1] == cacheV[j][0]+cacheV[j][1]) {
duplicate = true;
}
}
if(!duplicate){ //if origin/destination columns from sourceSheet are NOT matched on cacheSheet
array.push([sourceV[i][0], sourceV[i][1], sourceV[i][2], sourceV[i][3]]);//columns with new data
}
}
//add new data to last row of cacheSheet
cacheSheet.getRange(cacheSheet.getLastRow()+1, 1, array.length, 4).setValues(array);
}
One of the easiest solution thats coming to mind is Caching. Instead of making API calls everytime check if we have already made that call previously.
Something like this
function getTravelTime(origin, destination) {
var travelTime = getTravelTimeFromPreviousCall(origin, destination);
if (travelTime != null) {
return travelTime;
} else {
var travelTime = fetchTravelTime(origin, destination);
storeTravelTime(origin, destination, travelTime);
return travelTime;
}
}
function fetchTravelTime(origin, destination) {
var API_KEY = PropertiesService.getScriptProperties().getProperty('keyMaps');
var baseUrl = "https://maps.googleapis.com/maps/api/distancematrix/json? units=imperial&origins=";
var queryUrl = baseUrl + "place_id:" + origin + "&destinations=" + "place_id:" + destination + "&mode=driving" + "&key=" + API_KEY;
var response = UrlFetchApp.fetch(queryUrl);
var json = response.getContentText();
var time = JSON.parse(json);
return time.rows[0].elements[0].duration.text;
}
For this we can define our cache something like :
A sheet with column -
origin
destination
travel time
And we need to define following functions:
getTravelTimeFromPreviousCall(origin, destination) : In this we need to check cache and return travel time for that origin & destination, if not found then return null
storeTravelTime(origin, destination, time) : This will only store travel time for future use in cache sheet
You can try something like this :
function getTravelTimeFromPreviousCall(origin, destination) {
var cacheSheet = SpreadsheetApp.openById(SPREADSHEET_ID).getSheetByName(CACHE_SHEET_NAME);
var cacheData = sheet.getDataRange().getValues();
for (var i=0; i<cacheData.length; i++) {
if (cacheData[i][ORIGIN_COLUMN_INDEX]==origin && cacheData[i][DESTINATION_COLUMN_INDEX]==destination) {
return cacheData[i][TRAVEL_TIME_COLUMN_INDEX];
}
}
return null;
}
function storeTravelTime(origin, destination, travelTime) {
var cacheSheet = SpreadsheetApp.openById(SPREADSHEET_ID).getSheetByName(CACHE_SHEET_NAME);
sheet.appendRow([origin, destination, travelTime]);
}
Please fix loop variable, array indexes & constants, as per your sheet.

Filter one array using a 2nd array, and display the results on a spreadsheet

----- BACKGROUND ----- In Google Sheets/Apps Script, I'm trying to move some transaction data from one sheet and organize it on a different sheet with subtotal rows for each customer account (called "Units") with a grand total at the bottom.
I have the transactions organized in an array of objects as key:value pairs ("transactions", the keys are headers, some values are strings, others are numbers), and I have the units in a 2nd array called "unitList". I want to iterate through each element in the unitList array, and pull each transaction with a matching unit onto my "targetSheet", then append a subtotal row for each unit.
----- UPDATE 10/7/2018 3:49PM EST -----
Thanks to everyone for your input - I took your advice and ditched the library I was using and instead found better getRowsData and appendRowsData functions which I put directly in my code project. This fixed the array filter problem (verified by logging filterResults), BUT, now when I call appendRowsData(), I get this error:
The coordinates or dimensions of the range are invalid. (line 73, file "Display Transactions")
Line 73 is the line below, in the appendRowsData function. Any help on how to fix this would be greatly appreciated.
var destinationRange = sheet.getRange(firstDataRowIndex, 1, objects.length, 9);
Here's my project in it's entirety thus far:
function displayTransactions() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
// Label each sheet
var dashboard = ss.getSheetByName('Dashboard');
var unitsSheet = ss.getSheetByName('Unit Ref Table');
var transactionsSheet = ss.getSheetByName('Transactions Ref Sheet');
var targetSheet = ss.getSheetByName('Target Sheet');
// Returns true if the cell where cellData was read from is empty.
// Arguments:
// - cellData: string
function isCellEmpty(cellData) {
return typeof(cellData) == "string" && cellData == "";
}
// Define function that converts arrays into JSON
// For every row of data in data, generates an object that contains the data.
// Names of object fields are defined in keys.
// Arguments:
// - data: JavaScript 2d array
// - keys: Array of Strings that define the property names for the objects to create
function getObjects(data, keys) {
var objects = [];
for (var i = 0; i < data.length; ++i) {
var object = {};
var hasData = false;
for (var j = 0; j < data[i].length; ++j) {
var cellData = data[i][j];
if (isCellEmpty(cellData)) {
continue;
}
object[keys[j]] = cellData;
hasData = true;
}
if (hasData) {
objects.push(object);
}
}
return objects;
}
// Define function that pulls spreadsheet data into arrays, then converts to JSON using getObjects function
function getRowsData(sheet) {
var headersRange = sheet.getRange(1, 1, 1, sheet.getLastColumn());
var headers = headersRange.getValues()[0];
var dataRange = sheet.getRange(sheet.getFrozenRows()+1, 1, sheet.getLastRow(), sheet.getLastColumn());
return getObjects(dataRange.getValues(), headers);
}
// Define function appendRowsData that uses getLastRow to fill in one row of data per object defined in the objects array.
// For every Column, it checks if data objects define a value for it.
// Arguments:
// - sheet: the sheet object where the data will be written
// - objects: an array of objects, each of which contains data for a row
function appendRowsData(sheet, objects) {
var headersRange = sheet.getRange(7, 1, 1, 9);
var firstDataRowIndex = sheet.getLastRow() + 1;
var headers = headersRange.getValues()[0];
var data = [];
for (var i = 0; i < objects.length; ++i) {
var values = []
for (j = 0; j < headers.length; ++j) {
var header = headers[j];
values.push(header.length > 0 && objects[i][header] ? objects[i][header] : "");
}
data.push(values);
}
var destinationRange = sheet.getRange(firstDataRowIndex, 1, objects.length, 9);
destinationRange.setValues(data);
}
// Call getRowsData on transactions sheet
var transactions = getRowsData(transactionsSheet);
// Get array of units
var unitList = unitsSheet.getRange("B2:B").getValues();
// Iterate through the unitList and pull all transactions with matching unit into the target sheet
for (var i=0; i < unitList.length; i++) {
var subTotal = 0;
var grandTotal = 0;
var filterResults = transactions.filter(function(x) {
return x['Unit'] == unitList[i];
})
Logger.log(filterResults); // This brings the correct results!
// Display matching transactions
appendRowsData(targetSheet, filterResults);
// Grand total at the bottom when i=units.length
}
}

Resources