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);
}
Related
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'm using Google Scripts with the aim of calling an already filled cell using the ID. Once the data has been called, the user would be able to update and send it back to the main "data", in case some update is necessary.
The code isn't giving any error, but is not updating the cells either. See below an example of the code:
function Update() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var formS = ss.getSheetByName("App"); // From sheet
var dataS = ss.getSheetByName("Results"); // Get the data already inputted
var str = formS.getRange("D6").getValue(); // The ID to find and to update in the "Inputs" sheet
var values = dataS.getDataRange().getValues();
for (var i = 0; i<=values.length; i++) {
var rowFinder = values[i];
if (rowFinder[columnIndex] == str) {
var rowNumber = i+1;
var updatedValues = [[formS.getRange("D9").getValue(),
formS.getRange("D13").getValue(),
formS.getRange("D14").getValue(),
formS.getRange("D15").getValue(),
formS.getRange("D16").getValue(),
formS.getRange("D17").getValue(),
formS.getRange("D18").getValue(),
formS.getRange("D19").getValue()]];
dataS.getRange(rowNumber, 2, 1, 17).setValues(updatedValues);
}
break;
}
}
Find also an example of the sheet that I'm using, where you could also see the whole code (everything else is working fine). Spreadhsheet here
Why function is not updating the data? I've tried to put the dataS.getRange(rowNumber, 2, 1, 17).setValues(updatedValues); in different parts of the code and it doesn't work either. Any ideas?
It is best to get the values of a range all at once and iterate through the entire range than pulling individual cells.
I tested this on your sample spreadsheet (Thanks for including!)
function SubmitData() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var formS = ss.getSheetByName("App"); // Add the name of the sheet where the data is going to be retrieved
var dataS = ss.getSheetByName("Results"); // Name of the sheet where the data should appear
//get the entire form range
var values = formS.getRange(6, 3, 14, 2).getValues();
//Create an array to paste into the Results tab
var pasteArr =[];
//iterate through the two columns on the App tab, only add values to the paste array
//if Col C is not blank and does not equal "Choose Params"
for (var i in values) {
//the values array looks like this [[row1Col1, row1Col2],[row2Col1, row2Col2]..]
//so to get to the value in row i, column 1 you use values[i][0]
let field = values[i][0];
if (field != "" && field != "Choose Params") {
let val = values[i][1];
pasteArr.push(val);
}
};
Logger.log(pasteArr);
var dataLastRow = dataS.getLastRow() + 1;
//Build the range to paste in - use the length of pasteArr to get the proper number of columns
//then to make pasteArr a 2D array put pasteArr inside another array by using square brackets
dataS.getRange(dataLastRow, 1, 1, pasteArr.length).setValues([pasteArr]);
Logger.log("Done");
ClearCell();
}
Your other functions have similar issues.
function Update() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var formS = ss.getSheetByName("App"); // From sheet
var dataS = ss.getSheetByName("Results"); // Get the data already inputted
var dataVals = dataS.getDataRange().getValues();
//get the entire form range
var values = formS.getRange(6, 3, 14, 2).getValues();
//Create an array to paste into the Results tab
var pasteArr =[];
//iterate through the two columns on the App tab, only add values to the paste array
//if Col C is not blank and does not equal "Choose Params"
for (var i in values) {
let field = values[i][0];
if (field != "" && field != "Choose Params") {
let val = values[i][1];
pasteArr.push(val);
}
}
let numCols = pasteArr.length;
//iterate through the rows on the data tab, update the row with the matching ID
for (var j in dataVals) {
let dataTableId = dataVals[j][0];
let formID = pasteArr[0];
Logger.log(dataTableId + " , "+formID);
if (dataTableId === formID){
//have to convert j from 0 index to columns by adding 1. The + before the j and the 1 force app script to treat it as a number.
dataS.getRange(+j+ +1, 1, 1, numCols).setValues([pasteArr]);
Logger.log("dataTableId matches");
}
}
Logger.log("Done");
}
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
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 new to Google Apps Script and I'm trying to ignore the empty rows from a for loop, but I'm still getting the empty rows in my log. Here are my codes,
function getNonEmptyRows() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet1 = ss.getSheetByName("Bldng4");
var lr = sheet1.getLastRow() - 17;
for (var i = 1; i < lr; i++) {
var singleRow = sheet1.getRange(i, 1, 1, sheet1.getLastColumn()).getValues();
if (singleRow.length > 0) {
Logger.log(singleRow);
}
}
}
How can I get the only non empty rows from the loop? Need this help badly. Thanks.
var range_data =
sheet1.getRange("A2:A") //Column Range
.getValues() //Get array from range values
.filter(array=>array != '') //Filter non-empty values
I was looking for a solution to a similar problem and here is what I did:
first, I found this tutorial on how to create google apps script to eliminate duplicate rows.
next, i modified it like this to eliminate empty rows:
function removeEmptyRows() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("settings");
var data = sheet.getRange("A2:D").getValues();
var newData = new Array();
for(i in data){
var row = data[i];
var empty = false;
for(i in data){
if(row.toString() == ",,,"){
empty = true;
}
}
if(!empty){
newData.push(row);
}
sheet.getRange(2, 6, newData.length, newData[0].length).setValues(newData);
};
As you can see, it takes A2:D range, removes empty rows and then pastes filtered data without empty rows into range F2:I.
You can try to use this script, but you may need to adjust it to "width" of your array. To do so change the number of commas in the following string:
if(row.toString() == ",,,"){
edit:
I modified script a bit to automatically adjust to width of your array:
function removeEmptyRows() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("settings");
var range = sheet.getRange("A2:C");
var data = range.getValues();
var dataWidth = range.getWidth();
var newData = new Array();
if (dataWidth<=1) {
var stringToCompare = '';
}else{
var stringToCompare = ',';
for (var i=0;i<dataWidth-2;i++) stringToCompare+=","
};
for(i in data){
var row = data[i];
var empty = false;
for(i in data){
if(row.toString() == stringToCompare){
empty = true;
}
}
if(!empty){
newData.push(row);
}
}
sheet.getRange(2, 6, newData.length, newData[0].length).setValues(newData);
};
The getValues() method you are using always return a 2 dimensions array, whatever the contents of the cells might be. There are several ways to get the cells content, one of them is to stringify the range content (convert matrix to single string) , remove the commas (and eventually "invisible" spaces) and check the length of the resulting string.
replacement code could go like this :
var singleRow = sheet1.getRange(i, 1, 1, sheet1.getLastColumn()).getValues().toString().replace(',','').replace(' ','');
That said, this code is very inefficient because it uses SpreadSheetApp service in each loop iteration which is particularly slow.
You'll find better approches in the documentation about best practices.