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
}
});
}
Related
I have spreadsheet1 with all the details as in this image(Spreadsheet1), there are columns with startDate and endDate with some dates. Now i have a different spreadsheet2 like in this image (spreadsheet2) with header row of all the dates in the year (from 01/01/2021 to 31/12/2021). Now startDate and endDate from spreadsheet1 should match the header in spreadsheet2 and put the values of the column Type from spreadsheet1 to the respective cells in spreadsheet2 (like it is present in spreadsheet2 image for reference). Below is the code i'm working with but i'm not reaching my goal. Please help me i'm new to coding world. Thank you.
function myFunction() {
let ss = SpreadsheetApp.getActiveSpreadsheet();
let sheet = ss.getActiveSheet();
let last_row = sheet.getLastRow();
let data = sheet.getRange("A2:E"+last_row).getValues();
let start_date = [];
let end_date = [];
let dates_between = [];
let id = [];
let name = [];
let message = [];
let dd = SpreadsheetApp.openById('1z5WB1sACp1zvgfyXDbAmYxklSZOMIC8kNi_3Yci-PkM');
let dsheet = dd.getActiveSheet();
let dlast_row = dsheet.getLastRow();
let ddata = dsheet.getRange('C2:NC'+dlast_row).getValues();
let did = dsheet.getRange('A2:A'+last_row);
for(let i = 0; i<data.length;i++){
// let id = data[i][0];
id.push(data[i][0]);
name.push(data[i][1]);
start_date.push(data[i][2]);
end_date.push(data[i][3]);
message.push(data[i][4]);
dates_between.push(DATES_BETWEEN(start_date[i], end_date[i]));
}
did.setValue(id);
}
function DATES_BETWEEN(dateFrom, dateTo) {
var t = dateFrom.getTime(),
tMax = dateTo.getTime(),
values = [];
while (t <= tMax) {
values.push(new Date(t));
t += 24000 * 3600;
}
return values;
}
If you're able to put your 'types' on the row at start date and at end date you can fill the gap in-between with Array.fill() method.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/fill
All you need is to get the row as an array and to put it back on the sheet. You don't even need to calculate dates in the gap. You just fill all these empty elements between filled start and end cells.
Here is your row/array: ['','','x','','','x','','']
Start cell is array.indexOf('x')
End cell is array.lastIndexOf('x')
To fill the gap with 'x' strings use array.fill('x', start, end)
You will get: ['','','x','x','x','x','','']
Below is my solution that doesn't use Dates. If your dates have the same format on both sheets and if your destination sheet always has the dates of the rows of your source sheet you can consider them as strings, and use them as keys of an object (a map in my case):
function myFunction() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var src_sheet = ss.getSheetByName('Sheet1');
var dest_sheet = ss.getSheetByName('Sheet2');
// get dates from first row of destination sheet
var dates = dest_sheet.getDataRange().getDisplayValues()[0].slice(2);
// get rows (without first row) from source sheet
var rows = src_sheet.getDataRange().getDisplayValues().slice(1);
// loop through the rows and get the table
var table = rows.map(row => {
// get variables from the row
var [id, name, type, start, end] = row.slice(0, 5);
// create empty Map with dates-keys (date1:'', date2:'', ...)
var dates_map = new Map(dates.map(date => [date, '']));
// assign 'type' to key['start date'] and to key['end date']
dates_map.set(start, type).set(end, type);
// create array (row) from values of the Map
var row_array = Array.from(dates_map.values());
// fill empty elements of the array between first and last 'type'
row_array.fill(type, row_array.indexOf(type), row_array.lastIndexOf(type));
// return row
return [id, name, ...row_array];
});
// set the table on the destination sheet
dest_sheet.getRange(2, 1, table.length, table[0].length).setValues(table);
}
The same code without comments:
function myFunction() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var src_sheet = ss.getSheetByName('Sheet1');
var dest_sheet = ss.getSheetByName('Sheet2');
var dates = dest_sheet.getDataRange().getDisplayValues()[0].slice(2);
var rows = src_sheet.getDataRange().getDisplayValues().slice(1);
var table = rows.map(row => {
var [id, name, type, start, end] = row;
var dates_map = new Map(dates.map(date => [date,'']));
dates_map.set(start, type).set(end, type);
var row_array = Array.from(dates_map.values());
row_array.fill(type, row_array.indexOf(type), row_array.lastIndexOf(type));
return [id, name, ...row_array];
});
dest_sheet.getRange(2, 1, table.length, table[0].length).setValues(table);
}
Just in case, this is a destructuring assignment:
var [id, name, type, start, end] = row;
It means:
var id = row[0];
var name = row[1];
var type = row[2];
var start = row[3];
var end = row[4];
Here is the link to my dummy spreadsheet.
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 a list of 290 items with 4 columns, which I need to duplicate. I have in a Spreadsheet Row some departements and under it a lot of systems. for each system I need to duplicated the 290 values and add one index column at the front, the department in column 6 and the system in column 7.
I am using the following code:
const ssOrg = SS.getSheetByName("OrgStructure");
function myFunction() {
var afinal = [];
var aDevs = ssDeliverables.getDataRange().getValues();
aDevs.shift();
var lastRow = ssOrg.getLastRow();
var lastColum = ssOrg.getLastColumn();
var count = 1
for (var spalte = 1; spalte <lastColum; spalte++){
var squad = ssOrg.getRange(3,spalte).getValue();
for (var reihe=5; reihe <lastRow; reihe++) {
var system = ssOrg.getRange(reihe,spalte).getValue();
if (system !== ""){
aDevs.map(function(row){
row[0] = count;
row[5] = squad;
row[6] = system;
count ++
return row
})
Logger.log(system);
afinal = afinal.concat(aDevs);
}
}
}
var lastDataRow = ssAssessmentLogic.getLastRow();
ssAssessmentLogic.getRange(2,1,lastDataRow-1,10).clearContent();
var rngResult = ssAssessmentLogic.getRange(2,1,afinal.length,7);
rngResult.setValues(afinal);
}
The problem is that the array at the end (16000 rows) has the same value for each row in column 6 and 7. It is allways the last system & department combination that appears in all 16000 rows.
Where am I wrong?
The question was a little confusing for me but I followed your specifics in the comments section where you explain what exactly info to copy and how and where to paste it.
This gets the job done:
const ss = SpreadsheetApp.getActive();
// this gets the "deliverables" and the "departments", this last ones in a list
// and for each department runs the function to add the complete new modified array to the spreadsheet
function fillsNewSheet(){
var newSheet = ss.getSheetByName('List_DeliverablesALL');
// hardcoded the titles
newSheet.getRange(1, 1, 1, 6).setValues([['taskHasDeliverableNumber', 'Deliverable Description', 'SDP Task', 'SDP Milestone', 'Group', 'Department']])
var deliverables = getDeliverables();
var departments = getDepartments();
for(i=0;i<departments.length;i++){
var departmentsGroup = departments[i];
for(j=0;j<departmentsGroup.length;j++){
addsNewSection(deliverables, departmentsGroup[j])
}
}
}
// this just gets de array of values we are gonna paste for each department in the structure sheet.
function getDeliverables(){
var deliSheet =ss.getSheetByName('Deliverables1');
var deliValues = deliSheet.getRange(2, 2, deliSheet.getLastRow()-1, 4).getValues();
return deliValues;
}
// As the departments are in different columns with different row counts,
// I go over the whole columns and rows and create a single list with all "department" and "group" pairs
function getDepartments(){
var structureSheet = ss.getSheetByName('OrgStructure');
var cols = structureSheet.getLastColumn();
var groups = structureSheet.getRange(3, 1, 1, cols).getValues()[0]
var departments = [];
for(i=1;i<=cols;i++){
var group = groups[i-1];
var groupDeps = structureSheet.getRange(5, i, structureSheet.getLastRow(), 1).getValues();
var subDeps = []
for(j=0;j<groupDeps.length;j++){
subDeps.push([group, groupDeps[j][0]])
}
var filtered = subDeps.filter( function (data) { return data[1] != "" });
departments.push(filtered);
}
return departments;
}
// finally this gets the complete list of "deliverables" from the first sheet, and one specific department.
function addsNewSection(deliverables, department){
var newSheet = ss.getSheetByName('List_DeliverablesALL');
// and in every row of the deliverables list we add the corresponding department/group pair to get the new modified array.
var newSection = []
for(k=0;k<deliverables.length;k++){
var newRow = deliverables[k].concat(department)
newSection.push(newRow)
}
// when this is complete I paste the whole new array in the third sheet.
newSheet.getRange(newSheet.getLastRow()+1, 1, newSection.length, 6).setValues(newSection)
}
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 !