I have a Google Ads script which pulls close variant keyword performance if it is above a ROAS threshold, and then exports it to a google sheet every 30 days. I'm then looking for a way for the script to take the keyword and ad group data and loop through the sheets, lookup the keywords and return the quality scores for these keywords after I have added them.
Here's my below code, I'm struggling with adding to a 2D array from the Google sheet (as you need a 2d array to look up keywords). I also need to be able to push the data back to the sheet. Any thoughts please?
var sheets = SpreadsheetApp.openByUrl(spreadsheetUrl).getSheets();
for (i = 0; i < sheets.length; i++) {
var sheet = sheets[i];
var data_range = sheet.getDataRange();
var last_row = data_range.getLastRow();
for(var r=2;r<=last_row;r++) {
var list = [];
var keywordID = [];
var adGroupId = [];
var keywordID = data_range.getCell(r,13).getValue();
var adGroupId = data_range.getCell(r,14).getValue();
list.push([adGroupId,keywordID]);
var keywords = AdsApp.keywords().withIds(list).get();
while (keywords.hasNext()) {
var keyword = keywords.next();
var stats = [];
var stats = keyword.getQualityScore();
row_array.push(stats);
sheet.appendRow(row_array)
}
}
}
var data_range = sheet.getDataRange(); is your 2D range
var data = sheet.getDataRange().getValues(); is your 2D array
You can loop through the data this way (no need the last_row):
for(var r=2; r<=data.length; r++)
Then
var keywordID = data[r][13]; // or [12], I don't know
var adGroupId = data[r][14]; // or [13]
Probably it would look about like this
var sheets = SpreadsheetApp.openByUrl(spreadsheetUrl).getSheets();
for (i = 0; i < sheets.length; i++) {
var sheet = sheets[i];
var data = sheet.getDataRange().getValues();
for (var r = 2; r <= data.length; r++) {
var keywordID = data[r][13]; // or [12]
var adGroupId = data[r][14]; // or [13]
var keywords = AdsApp.keywords().withIds([adGroupId, keywordID]).get(); // ?
var row_array = []
while (keywords.hasNext()) {
var stats = keywords.next().getQualityScore();
row_array.push(stats);
}
sheet.appendRow(row_array);
}
}
I haven't tested it since I have no your data and not quite understand the task.
Related
I have the script mostly working- except the data being pushed actually says "Range" I must be missing something- can you not set values across a range?
function up4Grabs() {
var sheet1 = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Sheet1');
var sheet2 = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Final');
var destsheet = SpreadsheetApp.openById("MYID GOES HERE").getSheetByName('Items');
var destLastRow = destsheet.getLastRow();
var destRange = destsheet.getRange(1,9,destLastRow);
var dataLastRow = sheet1.getLastRow();
var dataRange = sheet1.getRange(1,9,dataLastRow);
var data = dataRange.getValues();
for(var i = 0; i < data.length; i++) {
if (data[i] > 0) {
var targetLastRow = destsheet.getLastRow() + 1;
var test = destsheet.getRange(1,9,targetLastRow);
sheet1.getvalues(test).setValues(sheet1.getRange(i+1,1,1,9))
}
}
}
Problem:
setValue() setting "Range" rather than actual values.
Cause:
You're passing a range rather than a value to the setValue() in the first place.
Solution:
function up4Grabs() {
var sheet1 = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Sheet1');
var sheet2 = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Final');
var destsheet = SpreadsheetApp.openById("MY ID GOES HERE").getSheetByName('Items');
var destLastRow = destsheet.getLastRow();
var destRange = destsheet.getRange(1,9,destLastRow);
var dataLastRow = sheet1.getLastRow();
var dataRange = sheet1.getRange(1,9,dataLastRow);
var data = dataRange.getValues();
for(var i = 0; i < data.length; i++) {
if (data[i] > 0) {
var targetLastRow = destsheet.getLastRow() + 1;
var values = sheet1.getRange(i+1,1,1,9).getValues();
destsheet.getRange(1,9,values.length,9).setValues(values);
}
}
}
Since you're setting the values of a whole range, you need to use setValues() rather than setValue(), note: plural rather than singular. I've also added getValues() to the range you were already pulling, this returns the array of values within the range that can then be passed to setValues().
So, I'm not much of a coder to be honest, but I've managed to fumble my way through counting cell background colour, but struggling to get it to work for counting cells where the font is bold. I've detailed my function below, which counts only 6 cells with a bold font style, but there is 13 cells with a bold font style.
function countboldcells() {
var book = SpreadsheetApp.getActiveSpreadsheet();
var sheet = book.getActiveSheet();
var range_input = sheet.getRange("E2:S7");
var range_output = sheet.getRange("G14");
var cell_styles = range_input.getFontStyle();
var count = 0;
for(var r = 0; r < cell_styles.length; r++) {
for(var c = 0; c < cell_styles[0].length; c++) {
if(cell_styles.isBold = true) {
count = count + 1;
}
}
range_output.setValue(count);
}
}
Your if statement needs to have 3 "=" inside the parentheses
if(cell_styles.isBold === true)
getFontWeights() is the method that will return bold or not. Then the easy way to count them would be to flatten the array, filter all of the "bold" elements and get the length of the filtered list
function countboldcells() {
var book = SpreadsheetApp.getActiveSpreadsheet();
var sheet = book.getActiveSheet();
var range_input = sheet.getRange("E2:S7");
var range_output = sheet.getRange("G14");
// Get the fontWeights of the range and flatten the array
var cell_styles = range_input.getFontWeights().join().split(",");
// Filter out any value that is not "bold"
var filter_bold = cell_styles.filter(function (e) { return e == "bold" });
// Set the count
range_output.setValue(filter_bold.length);
}
Here is your code with corrections. Explanations are in the comments.
function countboldcells() {
var book = SpreadsheetApp.getActiveSpreadsheet();
var sheet = book.getActiveSheet();
var range_input = sheet.getRange("E2:S7");
var range_output = sheet.getRange("G14");
var cell_styles = range_input.getFontWeights(); // getFontStyle can only return 'italic' or 'normal'
var count = 0;
for(var r = 0; r < cell_styles.length; r++) {
for(var c = 0; c < cell_styles[0].length; c++) { // isBold is a method for Google Documents only (not sheets)
if(cell_styles[r][c] === "bold") { // you need at least two '=' signs // also include the index of cell_styles
count = count + 1; // count += 1 would also work
}
}
}
range_output.setValue(count); // make sure you setValue only when booth loops are done.
}
I currently have a list with two columns. The first column is student name, and the second column is the number of points they have.
I imported this list from multiple spreadsheets so there were many duplicates on the names of the students. I am able to remove the duplicates, but I want to keep a tally on the total points they have. For example:
Amy 10
Bob 9
Carol 15
Amy 12
would turn into:
Amy 22
Bob 9
Carol 15
This is what I have so far:
var target = SpreadsheetApp.getActiveSpreadsheet();
var sheet = target.getSheetByName("Sheet2");
var data = sheet.getRange("A2:B1000").getValues();
var newData = new Array();
var k = 0
var finallist = []
for(i in data){
k++;
var row = data[i];
var duplicate = false;
for(j in newData){
if(row[0] == newData[j][0]){
duplicate = true;
var storedHour = sheet.getRange("B"+k).getValue();
var position = finallist.indexOf(row[0]);
var originalCell = sheet.getRange("B"+(position+1));
var originalHour = originalCell.getValue();
originalCell.setValue(originalHour + storedHour);
sheet.getRange(k,2).setValue("")
sheet.getRange(k,1).setValue("")
}
}
if(!duplicate){
newData.push(row);
finallist.push(row[0])
}
}
}
The problem I'm having is that we have a really large data sample and I'm afraid it may run over Google's 5 minute maximum execution time. Is there another more efficient way to achieve my goal?
Your code is running slow because Spreadsheets API methods (like getRange) are time consuming and much slower then other JavaScript code.
Here is optimized function with reduced number of such Spreadsheets API calls:
function calcNumbers()
{
var target = SpreadsheetApp.getActiveSpreadsheet();
var sheet = target.getSheetByName("Sheet2");
var lastRow = sheet.getLastRow();
var dataRange = sheet.getRange(2, 1, lastRow-1, 2);
var data = dataRange.getValues();
var pointsByName = {};
for (var i = 0; i < data.length; i++)
{
var row = data[i];
var curName = row[0];
var curNumber = row[1];
// empty name
if (!curName.trim())
{
continue;
}
// if name found first time, save it to object
if (!pointsByName[curName])
{
pointsByName[curName] = Number(curNumber);
}
// if duplicate, sum numbers
else
{
pointsByName[curName] += curNumber;
}
}
// prepare data for output
var outputData = Object.keys(pointsByName).map(function(name){
return [name, pointsByName[name]];
});
// clear old data
dataRange.clearContent();
// write calculated data
var newDataRange = sheet.getRange(2, 1, outputData.length, 2);
newDataRange.setValues(outputData);
}
Sorting before comparing allows looking at the next item only instead of all items for each iteration. A spillover benefit is finallist result is alphabatized. Execution time reduction significant.
function sumDups() {
var target = SpreadsheetApp.getActiveSpreadsheet();
var sheet = target.getSheetByName("Sheet2");
var data = sheet.getRange("A2:B" + sheet.getLastRow()).getValues().sort();
var finallist = [];
for(var i = 0; i<= data.length - 1; i++){
var hours = data[i][1];
while((i < data.length - 1) && (data[i][0] == data[i+1][0])) {
hours += data[i+1][1];
i++;
};
finallist.push([data[i][0], hours]);
};
Logger.log(finallist);
}
Edit: the simple data structure with the name being in the first column allows this to work. For anything more complex understanding and applying the methods shown in #Kos's answer is preferable
When I run my (messy) script it seems to run one extra time than required. i.e. below the last row it creates a pdf and marks a cell as processed.
The second issue is that the URLs don't seem to line up correctly with the name that it should be.
I appreciate the code is very messy but I'm happy to explain the reasoning for any part if it doesn't make sense.
Thanks in advance for any help!
The spreadsheet can be found here:
https://docs.google.com/spreadsheets/d/1nq5RRcwAKk9sFm6jVypFT_qJWcOGxFDZRv6ZpB-QClw/edit#gid=1194576382
and the code that's not working as expected is:
var ss = SpreadsheetApp.getActiveSpreadsheet()
var rawData = "rawData"
var practicePivot = "practicePivot"
var querySheet = "querySheet"
var pdfSheet = "pdfSheet"
var contactList = "contactList"
function createPDF(){
var sourceSheet = ss.getSheetByName("querySheet")
var pdfList = ss.getSheetByName("practicePivot")
var contactList = ss.getSheetByName("contactList")
var sourceRow = sourceSheet.getLastRow()
var sourceColumn = sourceSheet.getLastColumn()
var sourceStartRow = 4 //skips the headers and only pulls query data
var sourceStartColumn = 1
var sourceRange = sourceSheet.getRange(sourceStartRow, sourceStartColumn, sourceRow, sourceColumn)
var sourceValues = sourceRange.getValues()
var pdfLastRow = pdfList.getLastRow()
var storePracticeName = sourceSheet.getRange("A2").getValues()
var newSpreadsheet = SpreadsheetApp.create("Summary of Patients for" +storePracticeName)
sourceSheet.copyTo(newSpreadsheet, {contentsOnly: true})
newSpreadsheet.getSheetByName("sheet1").activate()
newSpreadsheet.deleteActiveSheet()
var pdfURLtemp = DriveApp.getFileById(newSpreadsheet.getId()).getAs("application/pdf")
var pdf = DriveApp.createFile(pdfURLtemp)
var pdfURL = pdf.getUrl()
Logger.log(pdfURL)
return pdfURL
}
function createQuery()
{
//Duplication check
var pdfCreated = "pdfCreated";
var pdfEmailed = "pdfEmailed";
var sheet = ss.getSheetByName("practicePivot");
var startRow = 2;
var lastRow = sheet.getLastRow();
var lastColumn = sheet.getLastColumn();
var dataRange = sheet.getRange(startRow, 1, lastRow, lastColumn) ;
var data = dataRange.getValues();
for (var i = 0; i < data.length; ++i)
{
var row = data[i];
var practiceName = row[0]
var pdfCheck = row[2]
var copySelection = sheet.getRange(startRow + i, 1)
var copyData = copySelection.getValues()
var copyLocation = ss.getSheetByName("querySheet")
var copyCell = copyLocation.getRange("A2")
if (pdfCheck != pdfCreated)
{
var pdfURL = createPDF()
copyCell.copyTo(copyData)
sheet.getRange(startRow + i, 4).setValue(pdfURL)
sheet.getRange(startRow + i, 3).setValue(pdfCreated)
SpreadsheetApp.flush()
}
}
}
Your start row is 2:
var startRow = 2;
Your loop is looping until the count of the last row. So if there were 10 rows, and the data starts in row 2, then you need the loop to run 9 times. But your loop is running 10 times.
So you need to adjust the stop for the loop.
var L = data.length - 1;//The number of rows in the data - not the sheet
for (var i = 0; i < L; ++i)
I am trying to send pathitems to the back of the layer via their selection order, but for some reason as the loop iterates though the selection[i] index it doesn't return them in order
var openDoc = app.activeDocument;
for (var i = 0; i < 3; i++){
var topObject = app.activeDocument.selection[i];
var activelayer = topObject.zOrder(ZOrderMethod.SENDTOBACK);
}
but if I try to unfold the loop it works:
var openDoc = app.activeDocument;
var topObject = app.activeDocument.selection[0];
var activelayer = topObject.zOrder(ZOrderMethod.SENDTOBACK);
var topObject2 = app.activeDocument.selection[1];
var activelayer = topObject.zOrder(ZOrderMethod.SENDTOBACK);
What am i doing wrong?