I have two functions in a google sheet that are meant to loop through a single column containing the file IDs of files in google drive, issuing a Properties.get to retrieve a single property "LastReview" for each document and paste all of the LastReview times in the next available column.
I'm having trouble getting the loop in "loopForMetadata" to work. I want it to acquire a list of all the LastReview times associated with each fileID and then post that to the next available column so that all of the LastReview times align with the fileIDs.
function getProperty(fileId) {
var propertyKey = 'LastReview'
var fileId = '1UaQkJU8r1kE9sxpFg6OD8aOuUCoRnSpB9Agg_R9HJ3s'
var response = JSON.stringify(Drive.Properties.get(fileId, 'LastReview', { visibility: 'PUBLIC' }).value);
var key = "value";
var resposeNoQuote = response.replace(/\"/g, "")
Logger.log(resposeNoQuote);
}
function loopForMetadata() {
var columnNeeded, data, lastColumn, sh;
sh = SpreadsheetApp.getActiveSheet();
lastColumn = sh.getLastColumn();
data = sh.getRange(1, 1, 1, lastColumn).getValues();//Get 2D array of all values in row one
data = data[0];//Get the first and only inner array
columnNeeded = data.indexOf('ID') + 1;//Arrays are zero indexed- add 1
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, columnNeeded, lastRow - 1, 1);
// Get array of values in the search Range
var rangeValues = searchRange.getValues();
// Loop through array and if condition met, add relevant
// background color.
var resultValues = []
for (i = 0; i < rangeValues.length; i++) {
resultValues.push(getProperty(rangeValues[i]));
Utilities.sleep(10);
}
Logger.log(resultValues);
};
I believe your goal as situation as follows.
You want to retrieve the values using the key of LastReview in the property from the files of fileId.
You want to put the retrieved values to the same row of fileId in the next available column.
You want to achieve this using Google Apps Script.
Modification point:
In your script,
getProperty() doesn't return the values.
resultValues is not put to the Spreadsheet.
When you want to retrieve the value of the key LastReview, Drive.Properties.get(fileId, 'LastReview', { visibility: 'PUBLIC' }).value directly returns the value.
As an important point, when the file of fileId has not property of the key LastReview, it seems that Drive.Properties.get() occurs an error. So in this modification, as a simple workaround, I used the try catch.
When above points are reflected to your script, it becomes as follows.
Sample script:
function loopForMetadata() {
var columnNeeded, data, lastColumn, sh;
sh = SpreadsheetApp.getActiveSheet();
lastColumn = sh.getLastColumn();
data = sh.getRange(1, 1, 1, lastColumn).getValues();//Get 2D array of all values in row one
data = data[0];//Get the first and only inner array
columnNeeded = data.indexOf('ID') + 1;//Arrays are zero indexed- add 1
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, columnNeeded, lastRow - 1, 1);
var rangeValues = searchRange.getValues();
var resultValues = []
// I modified below script.
for (i = 0; i < rangeValues.length; i++) {
var fileId = rangeValues[i];
var value = "";
try {
value = Drive.Properties.get(fileId, 'LastReview', { visibility: 'PUBLIC' }).value;
} catch(e) {}
resultValues.push([value]);
// Utilities.sleep(10); // I'm not sure whether this is required.
}
sheet.getRange(2, lastColumn + 1, resultValues.length, 1).setValues(resultValues);
Logger.log(resultValues);
};
Note:
Please confirm whether Drive API is enabled at Advanced Google services, again.
Reference:
Properties: get
Related
I am a very beginner, so I am sorry for asking a low-leveled question.
I want to drag out values on each merged cell (8 cells for each and listed down on a spreadsheet), and want to put them in a pull-down of google form.
I just tried to see values on the sheet but found out there was an error.
If someone know how to fix this, I am glad to hear that.
Error
TypeError: Cannot read property 'getRange' of undefined
updateFormList # Code.gs:13
function updateFormList() {
var formId = '---'
var sheetId = '---'
var sheetName = 'Sheet1'
var range = sheet.getRange("A3:A10");
var mergedRanges = range.getMergedRanges();
for (var i = 0; i < mergedRanges.length; i++) {
Logger.log(mergedRanges[i].getA1Notation());
Logger.log(mergedRanges[i].getDisplayValue());
}
}
To fix the error you have to assign a Class Sheet object to the variable named sheet.
function updateFormList() {
var formId = '---'
var sheetId = '---'
var sheetName = 'Sheet1'
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet(); // this works in scripts bounded to spreadsheet
var sheet = spreadsheet.getSheetByName(sheetName);
var range = sheet.getRange("A3:A10");
var mergedRanges = range.getMergedRanges();
for (var i = 0; i < mergedRanges.length; i++) {
Logger.log(mergedRanges[i].getA1Notation());
Logger.log(mergedRanges[i].getDisplayValue());
}
}
Regarding
I want to drag out values on each merged cell (8 cells for each and listed down on a spreadsheet), and want to put them in a pull-down of google form.
Merged cells might be helpful for data visualization, i.e. creating a report, but they might need more complex scripts and formulas. As a "very beginner" and when programming efficiency be important, whenever you can avoid having merged cells in your spreadsheets, specially if you will have to use formulas and scripts on their content.
Resources
https://developers.google.com/apps-script/guides/sheet
Try this:
function updateFormList() {
var formId = '---'
var sheetId = '---'
var sheetName = 'Sheet1'
const sheet = SpreadsheetApp.getActive().getSheetByName(sheetName);
var range = sheet.getRange("A3:A10");
var mergedRanges = range.getMergedRanges();
for (var i = 0; i < mergedRanges.length; i++) {
Logger.log(mergedRanges[i].getA1Notation());
Logger.log(mergedRanges[i].getDisplayValue());
}
}
I want to create a new worksheet each time I have a new user details in column 1 of my USERS sheet. Here is the code I have so far:
// Get the data from the sheet called CreateSheets
var sheetNames = SpreadsheetApp.getActive().getSheetByName("USERS").getDataRange().getValues();
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("USERS");
var data = ss.getDataRange().getValues();
var lr = ss.getLastRow();
var dataRange = ss.getRange(1, 1, lr, 1);
// Fetch values for each row in the Range.
var data = dataRange.getValues();
for (var i = 0; i < data.length; ++i) {
var row = data[i];
// For each row in the sheet, insert a new sheet and rename it.
sheetNames.forEach(function(row) {
var sheetName = data;
var sheet = SpreadsheetApp.getActive().insertSheet();
sheet.setName(sheetName);
});
}}
The code works but it is combining the data in the cells in column 1 into the name of the new spreadsheet. Thanks
function myfunc() {
const ss = SpreadsheetApp.getActive();
const ush = ss.getSheetByName("USERS");
const names = ush.getRange(1, 1, ush.getLastRow(), 1).getValues().flat();
const enames = ss.getSheets().map(s => s.getName());
names.forEach(n => {
if (~e.names.indexOf(n)) {
ss.insertSheet().setName(n);
enames.push(n); //add name to array to avoid duplicate names in column
}
});
}
Updated to account for existing sheet names and potential duplicate names in column 1.
I think this should do what you want. You should probably build in some sort of check to ensure the sheet doesn't already exist.
function makeSheetHappen() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var wsUser = ss.getSheetByName("USERS");//the sheet with users
//this gets all values in column 1 to end of spreadsheet (it might include blanks)
//flat function avoids having to pull 2-dim array (h/t COOPER!)
var sheetNames = wsUser.getRange(1, 1, wsUser.getLastRow(), 1).getValues().flat();
//mapping function to get an array of the spreadsheet's sheet names.
var theExistingNames = ss.getSheets().map(function (aSheet) {
return aSheet.getName();
});
//loops through the sheetNames and ensures not blank and not currently existing.
sheetNames.forEach(function (aName) {
if (aName != '' && !theExistingNames.includes(aName)) {
var newSheet = ss.insertSheet();
newSheet.setName(aName);
theExistingNames.push(aName); //add name to array to avoid duplicate names in column
}
});
}
I want to remove duplicates across 2 different sheets.
I have my active sheet, and I want to remove duplicates that already exist in my sheet "Blacklist". I want to run this process for both Column A and Column B (or simply for any values across the entire sheets). When a duplicate is found, I want to leave the row in tact but replace the value with '' (e.g. an empty cell).
I have a working version I mangled together, but only for the active sheet.
N.B. it's the findDuplicate function that I use, the removeDuplicate function I left there not to mess anything up :)
// this is a Google Apps Script project
function onOpen() {
var spreadsheet = SpreadsheetApp.getActive();
var menuItems = [
{ name: 'Find duplicates...', functionName: 'findDuplicate' },
{ name: 'Remove duplicates...', functionName: 'removeDuplicate' }
];
spreadsheet.addMenu('Duplicates', menuItems);
}
function removeDuplicate() {
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var sheet = SpreadsheetApp.getActiveSheet();
var range = sheet.getActiveRange();
var data = range.getValues();
var rowNum = range.getRow();
var columnNum = range.getColumn();
var columnLength = data[0].length;
var uniqueData = [];
var duplicateData = [];
// iterate through each 'row' of the selected range
// x is
// y is
var x = 0;
var y = data.length;
// when row is
while (x < y) {
var row = data[x];
var duplicate = false;
// iterate through the uniqueData array to see if 'row' already exists
for (var j = 0; j < uniqueData.length; j++) {
if (row.join() == uniqueData[j].join()) {
// if there is a duplicate, delete the 'row' from the sheet and add it to the duplicateData array
duplicate = true;
var duplicateRange = sheet.getRange(
rowNum + x,
columnNum,
1,
columnLength
);
duplicateRange.deleteCells(SpreadsheetApp.Dimension.ROWS);
duplicateData.push(row);
// rows shift up by one when duplicate is deleted
// in effect, it skips a line
// so we need to decrement x to stay in the same line
x--;
y--;
range = sheet.getActiveRange();
data = range.getValues();
// return;
}
}
// if there are no duplicates, add 'row' to the uniqueData array
if (!duplicate) {
uniqueData.push(row);
}
x++;
}
}
function findDuplicate() {
var sheet = SpreadsheetApp.getActiveSheet();
var range = sheet.getActiveRange();
var data = range.getValues();
var rowNum = range.getRow();
var columnNum = range.getColumn();
var columnLength = data[0].length;
var uniqueData = [];
// iterate through each 'row' of the selected range
for (var i = 0; i < data.length; i++) {
var row = data[i];
var duplicate = false;
// iterate through the uniqueData array to see if 'row' already exists
for (var j = 0; j < uniqueData.length; j++) {
if (row.join() == uniqueData[j].join()) {
// if there is a duplicate, highlight the 'row' from the sheet
duplicate = true;
var duplicateRange = sheet.getRange(
rowNum + i,
columnNum,
1,
columnLength
);
duplicateRange.setValue('');
}
}
// if there are no duplicates, add 'row' to the uniqueData array
if (!duplicate) {
uniqueData.push(row);
}
}
}
Thanks so much for your help! I've been at this for a few hours and figured I should just ask the experts for advice :)
The first lines of both your removeDuplicate and findDuplicate function seems indeed to indicate that you refer to the active spreadsheet / sheet / range
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var sheet = SpreadsheetApp.getActiveSheet();
var range = sheet.getActiveRange();
var data = range.getValues();
If you want to be able to use the same function for a given spreadsheet / sheet / range which is not the active one, you will need to use other functions than the getActiveXXX().
For example, to get the sheet named "Blacklist", you should use
sheet = spreadsheet.getSheetByName("Blacklist")
(see also https://developers.google.com/apps-script/reference/spreadsheet/spreadsheet#getsheetbynamename)
If you want to access a specific range which differs from the active range, you should use the getRange method (see also https://developers.google.com/apps-script/reference/spreadsheet/spreadsheet#getrangea1notation)
Note that getRange method can be used in different ways, e.g.
getRange("A1:D4"), getRange(1, 1, 3, 3) (the parameters being respectively startRow, startColumn, numRows,numColumns)
Additionally, if you don't want to hardcode the last line of your 2 columns, you will most probably need this function to find the last line in the code :
https://developers.google.com/apps-script/reference/spreadsheet/spreadsheet#getlastrow
(there is also an example there showing how to use getRange() in combination with getLastRow()).
I hope this will help you going further.
Please note that I didn't check the rest of your code and just assumed that your deduplication logic works fine as you mentioned it in your commment.
Good luck !
I'm trying to create two headers in Google Sheets with GAS.
I would like to write to each cell starting at A1 write the first string in the array "values" then move on to cell B1 and write the second string in array "values". And so on depending on the size of the array "values".
What am I doing wrong?
function formatSheet(){
var name = "Formatted Sheet";
var values = [["Description", "Quantity"]];
ss.setActiveSheet(ss.getSheets()[0]);
ss.renameActiveSheet(name);
createHeaders(name,values);
}
function createHeaders(name,values){
var sheet = ss.getSheetByName(name);
for(var i = 1; i < values.length; ++i){
for(j=0; j<values.length; ++j){
//create the headers
sheet.getRange(1, i).setValues(values[j]);
}
}
Maybe I'm misunderstanding what you're trying to do, but it looks like you've over-complicated things. If you're only setting the headers, you know that there will only be 1 array inside the values array - which means you can do away with the for loops and just grab the length of the first array inside. See my example:
function formatSheet(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var name = "Format2";
var values = [["Description", "Quantity"]];
ss.setActiveSheet(ss.getSheets()[0]);
ss.renameActiveSheet(name);
var sheet = ss.getSheetByName(name);
var cols = values[0].length;
sheet.getRange(1, 1, 1, cols).setValues(values);
}
It would also be helpful to know what error you were getting.
modifed Jens Astrup's solution to insert headers to currently active spreadsheet
function SetHeadersToActiveSheet(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = SpreadsheetApp.getActiveSheet();
var values = [["Description", "Quantity", "Amount"]];
var cols = values[0].length;
sheet.getRange(1, 1, 1,cols).setValues(values);
}
In an attempt to stream line some processes I have attempted to create a script to move a paper process into a google form. I've nhever really tinkered with Javascript but I have been following the trail of errors down to this one which I cannot seem to shake.
Currently I'm trying to define the range of data but keep getting the error mentioned in the title. Am I incorrectly calling the sheet in the script? I cannot seem to figure out how to properly define them.
Below is the section that defines the variables and sheets. Anything I'm doing incorrectly?
function sendEmail() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
Logger.log(ss.getName());
var sheet = ss.getActiveSheet()[0];
var startRow = 2;
// First row of data to process
var numRows = 12;
// Number of rows to process
var dataRange = ss.getRange(startRow, 2, numRows, 12)
//Assigning spreadsheet feilds
var data = dataRange.getValues();
for (i in data) {
var row = data[i];
var firstName = row[1];
var guestFirstN = row[6];
var guestLastN = row[7];
var arrivalDate = row[8];
var numberNights = row[9];
var rmName = row[10];
var rmAgree = row[11];
You first need to define the Sheet you want to get the data from since a Spreadsheet can have multiple Sheets.
Try replacing
var dataRange = ss.getRange(startRow, 2, numRows, 12)
with
var dataRange = ss.getActiveSheet().getRange(startRow, 2, numRows, 12);