How do you create a Uniq ID in Google Sheets using apps script? - google-apps-script

Im looking at creating a simple Uniq ID after column A has information entered and need for the ID to show on column I. I want to call it Trip Number and display Driver-John0001 and so forth using google sheets script. Sorry I'm new to this so I don't know the lingo
The current code I had found works but now I need it a bit different. This is what my results show Driver:1611710811706
I would like to pull Driver-John0001. Where the name John is generated by the column labeled Driver or column D
How do I change it to add the value on column D + 4 digit numbers that don't repeat?
function TripID () {
{ var sheetVals = [
["DriverLog","Driver:","",9,1]
];}
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
//Loop through every sheet value
for(var i = 0; i < sheetVals.length; i++){
var sheetID = sheetVals[i][0],
frontStr = sheetVals[i][1],
backStr = sheetVals[i][2],
IDcol = sheetVals[i][3],
editCol = sheetVals[i][4];
var offset = IDcol - editCol;
if(sheet.getName() === sheetID){
var date = new Date().getTime();
var newID = frontStr+date+backStr;
//Check the location of the active cell
var selectedCell = ss.getActiveCell();
if( selectedCell.getColumn() === editCol) {
//Update the ID Column
var cellToChange = selectedCell.offset(0,offset);
if(cellToChange.isBlank()){
cellToChange.setValue(newID);
};
};
};
};
};

Assuming that you will never need more than 9999 IDs, then you could modify your code with the following steps to accomplish this. Note that this solution will allow you to create a (nearly) unlimited number of ID numbers, but after 9999, the length of the id numbers will vary.
Use the PropertiesService to initialize a counter for creating IDs
function createIdProperty() {
PropertiesService.getScriptProperties().setProperty('idCounter', '0');
}
// run this once to create the idCounter property
createIdProperty();
// To test that your property was created you can run this
Logger.log(PropertiesService.getScriptProperties().getProperty('idCounter')); // logs '0'
Create a function to get the next counterId number from Script Properties as a string with 0s replacing empty digits ( 1 => '0001', 2 => '0002', 3 => '0003')
function getUniqueIdNumber() {
var currentIdNum = +PropertiesService.getScriptProperties().getProperty('idCounter'); //get counter as a number
currentIdNum++; // increment counter by 1
currentIdNum+=''; // convert counter back to a string
PropertiesService.getScriptProperties().setProperty('idCounter', currentIdNum); // store the new counter in properies
while (currentIdNum.length < 4) {
currentIdNum = '0'+currentIdNum;
}
return currentIdNum;
}
modify your original code to use getUniqueIdNumber instead of date for creating your id
Change
var newID = frontStr+date+backStr;
To
var idNumber = getUniqueIdNumber();
var newID = frontStr+idNumber;
Note that you can replace frontStr and backStr with any variables you want.

Related

If column contains text add a checkbox in column apps script google sheets

I am copying data from a spreadsheet titled after the specific month and placing it in my main spreadsheet. I have successfully copied the data into range K80:K94 on my Daily Hub sheet.
In range K80:K94 I now want to add a checkbox in column M if there is a value in column K. For example if there is a value in K80 and K81 there would be a checkbox in M80 and M81. I feel like this should be fairly straightforward, however I have tried a few different options including using IsBlank() and nothing seems to be working.
function dailyhubhabits() {
var montha = new Array(12);
montha[0] = "JANUARY";
montha[1] = "FEBRUARY";
montha[2] = "MARCH";
montha[3] = "APRIL";
montha[4] = "MAY";
montha[5] = "JUNE";
montha[6] = "JULY";
montha[7] = "AUGUST";
montha[8] = "SEPTEMBER";
montha[9] = "OCTOBER";
montha[10] = "NOVEMBER";
montha[11] = "DECEMBER";
var dailyhabitshubmonth = new Date();
var getdhmonth = montha[dailyhabitshubmonth.getMonth()];
Logger.log(getdhmonth);
var mhs = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(getdhmonth);
var monthhabitsogdata = mhs.getRange("C56:E70");
var gethabits = monthhabitsogdata.getValues();
Logger.log(gethabits);
var dhs = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("DAILY HUB");
var habitsarea = dhs.getRange("K80:K94");
monthhabitsogdata.copyTo(habitsarea);
//THIS IS WHERE I AM HAVING TROUBLE
var datavalues = dhs.getRange("K80:K94").getValues();
var data_leng = datavalues.length;
for(var i=0; i<data_leng; i++) {
if(datavalues[i][0].length != 0) {
dhs.getRange(i+1,14).insertCheckboxes();
}
}
}
You want to insert a checkbox on Column M when there is a value in the same row of column K.
There are two problems with this part of your script:
evaluating whether the cell has a value
defining the target range for the checkbox
Does the cell have a value?
length returns the number of records in an array, but it is not a good method for determining whether a cell contains a value. This is a popular topic; you might care to read Google Spreadheets Scripts: check if cell is empty for several methods.
a better approach is !== ""
Defining the target cell
dhs.getRange(i+1,14).insertCheckboxes(); - there are two problems here
Column M is 13
i starts at zero, so the first range value would be .getRange(1,14) = Cell N1.
so you need a variable that defines the startRow, such as:
var startRow = 80
REPLACE
//THIS IS WHERE I AM HAVING TROUBLE
var datavalues = dhs.getRange("K80:K94").getValues();
var data_leng = datavalues.length;
for(var i=0; i<data_leng; i++) {
if(datavalues[i][0].length != 0) {
dhs.getRange(i+1,14).insertCheckboxes();
}
}
WITH
var startRow = 80
var endRow = 94
var datavalues = dhs.getRange("K"+startRow+":K"+endRow).getValues()
var data_leng = datavalues.length;
for(var i=0; i<data_leng; i++) {
if(datavalues[i][0] !=="") {
dhs.getRange(i+startRow,13).insertCheckboxes()
}
}
SUGGESTION
In my understanding, here's your goal:
Check values in K80:K94
Insert a checkbox on a row in M that is adjacent to a row that isn't empty in the K80:K94 range.
Perhaps you could try this sample script to replace your current line on the section in inserting the check-boxes:
/** SUGGESTION
* 1. Iterate through the values in range K80:K94 & identify which aren't empty.
* 2. Get each non-empty values' row numbers.
* 3. To reduce runtime execution in the loop, if there are consecutive non-empty values, set them as a range (e.g. M80:M81). Otherwise a single value will be set as a single range (e.g. M83);
* 4. Iterate through these ranges & insert the checkboxes.
*/
var range = SpreadsheetApp.getActive().getRange('K80:K94');
var temp_values = range.getValues().map((x, i) => x != '' ? [x, (range.getLastRow() - (range.getNumRows() - i) + 1)].flat() : '*');
var ranges = temp_values.join().split('*').map(y => (y.replace(/[a-zA-Z,]+/g, '-')).split('-').filter(x => x != ''));
ranges.map(z => [...new Set([z[0], z[z.length - 1]])]).forEach(
row => row.length > 1 ? SpreadsheetApp.getActive().getRange(`M${row[0]}:M${row[1]}`).insertCheckboxes() :
SpreadsheetApp.getActive().getRange(`M${row[0]}`).insertCheckboxes()
);
/** End */
This sample script runs faster vs your current implementation as it shortens the data to be processed in the loop
Demo
Sample sheet
After running the script

Exception: The number of columns in the data does not match the number of columns in the range. The data has 0 but the range has 1

I am very new to javascript and have searched around a ton for this and can't seem to find the issue with my code. I am attempting to simply write a code that will copy the values in a column from a pivot table sheet in Google Sheet and then paste the values in another sheet. However, before pasting the values, I want each individual value to be duplicated 12 times (for 12 months). So, assuming I have 10 unique values (A, B, C, D, E, F, G, H, I, J) that I am copying, I want to return value A 12 times in a row, then value B 12 times in a row, etc.
I run getValues, which seems to put the values in a 2 dimensional array. I've then taken this temp_array that I had created and used a for loop to duplicate each value 12 times in a new array.
However, when I setValues, I am pasting the values in my spreadsheet correctly, but I get this error message regardless (The number of columns in the data does not match the number of columns in the range. The data has 0 but the range has 1.), any ideas why?
Here is a small example of what my input could look like (1st image) and what I would want the output to look like (2nd image)
function test2() {
// activating current spreadsheet for use
var spreadsheet = SpreadsheetApp.getActive();
//empty array
var array_dept_temp = [];
// returns cell position (ex: C5) of the last row of the pivot table 1 sheet that has content in column 1
var last_row = spreadsheet.getSheetByName("Pivot Table 1").getRange("A:A").getNextDataCell(SpreadsheetApp.Direction.DOWN).getRowIndex();
//subtracting 1 from last row because we are excluding the headers. This gives us our row_length
var row_length = last_row - 1
var array_dept = [[]]
array_dept = new Array(row_length*12)
//new Array(row_length*12);
// Get value in pivot table 1 from range of row 2 (dept name, but exclude the header), column 1, all the way to last row
// Then paste it in sheet5 from row 1, column 3, all the way to the last row defined above
array_dept_temp = spreadsheet.getSheetByName("Pivot Table 1").getRange(2,1, last_row).getValues();
for (var i = 1; i < row_length; i++ )
{
//get value and then paste it in a destination
array_dept.fill(array_dept_temp[i-1], (-12 + (12*i)) , 12*i);
}
var destination_dept = spreadsheet.getSheetByName("Sheet5").getRange(2,3,row_length*12);
destination_dept.setValues(array_dept);
}
Suggestion / Alternate solution:
Try:
function test() {
  var spreadsheet = SpreadsheetApp.getActive();
  var sheet = spreadsheet.getSheetByName("Pivot Table 1");
  var array_dept_temp = sheet.getRange(2,1, sheet.getLastRow()-1).getValues();
  var array_dept = [];
  for (var i = 0; i < array_dept_temp.length; i++) {
    array_dept = [...array_dept, ...Array.apply(null, Array(12)).map(function(){return array_dept_temp[i]})]
  }
  var destination_dept = spreadsheet.getSheetByName("Sheet5").getRange(2,3,array_dept.length);
  destination_dept.setValues(array_dept);
}
Result:
Another way without using fill or from.
Also some modification, you can just use .getLastRow() function to get the last row, however take not that if there is data below it will count all the rows including the blank until the row that has data. And you may also use .length on your data to setValue.
From your showing sample input and output situations, how about the following modified script?
Modified script:
function test2_sample() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var srcSheet = ss.getSheetByName("Pivot Table 1");
var dstSheet = ss.getSheetByName("Sheet5");
var srcValues = srcSheet.getRange("A2:A" + srcSheet.getLastRow()).getValues();
var dstValues = srcValues.flatMap(a => Array(12).fill(a));
dstSheet.getRange(2, 3, dstValues.length).setValues(dstValues);
}
When this script is run using your sample input sheet, I think that your expected output values are obtained.
Now, I thought that var dstValues = srcValues.flatMap(([a]) => Array(12).fill(a).map(e => [e])); can be modified to var dstValues = srcValues.flatMap(a => Array(12).fill(a));. This is simpler.
From your reply of Are you able to explain what this does? var dstValues = srcValues.flatMap(([a]) => Array(12).fill(a).map(e => [e]));, in this script, var dstValues = srcValues.flatMap(([a]) => Array(12).fill(a).map(e => [e])); can be also modified as follows. I thought that this might also help to understand it.
function test2_sample() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var srcSheet = ss.getSheetByName("Pivot Table 1");
var dstSheet = ss.getSheetByName("Sheet5");
var srcValues = srcSheet.getRange("A2:A" + srcSheet.getLastRow()).getValues();
var dstValues = [];
for (var i = 0; i < srcValues.length; i++) {
dstValues = dstValues.concat(Array(12).fill(srcValues[i]));
}
dstSheet.getRange(2, 3, dstValues.length).setValues(dstValues);
}
Note:
As additional information, when your showing script is modified, how about the following modification? In your script, I thought that it is required to add the values to array_dept in the loop. And, it is required to flatten the elements in the array.
function test2() {
var spreadsheet = SpreadsheetApp.getActive();
var array_dept_temp = [];
var last_row = spreadsheet.getSheetByName("Pivot Table 1").getRange("A:A").getNextDataCell(SpreadsheetApp.Direction.DOWN).getRowIndex();
var row_length = last_row - 1
var array_dept = []
array_dept_temp = spreadsheet.getSheetByName("Pivot Table 1").getRange(2, 1, last_row).getValues();
for (var i = 0; i < row_length; i++) {
array_dept = [...array_dept, ...Array(12).fill(array_dept_temp[i])];
}
var destination_dept = spreadsheet.getSheetByName("Sheet5").getRange(2, 3, array_dept.length);
destination_dept.setValues(array_dept);
}
Reference:
flatMap()

Vlookup + indexOf to find values in a CSV via Google App Script without using loop

The main idea is not to need looping to generate a VLOOKUP because it generates a huge slowdown when the amount of data is very large.
To VLOOKUP on data directly in the sheet I do as follows:
function myFunction() {
var s = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var searchValue = s.getRange("Test!A1").getValue();
var data = SpreadsheetApp.openById("XXXXXXXXXXXX").getSheetByName("Test 2");
var dataValues = data.getRange("A1:A").getValues();
var dataList = dataValues.join("ღ").split("ღ");
var index = dataList.indexOf(searchValue);
if (index === -1) {
s.getRange("Test!B1").setValue('off');
} else {
var row = index + 1;
var foundValue = data.getRange("D"+row).getValue();
s.getRange("Test!B1").setValue(foundValue);
}
}
But there is a big problem in this method, because when many different accounts try to access this sheet at the same time, the error type error: could not connect sheet xxxxx appears or causes huge delay sometimes.
So what was the solution I found? Publish spreadsheet pages as CSV so they can be used and this error doesn't happen when many accounts call the same spreadsheet.
Currently, as I haven't found a way to use indexOf using the first column when I import the CSV with several columns of data, I had to create a spreadsheet page only with the copy data of column A, and then I got to the final result of VLOOKUP like this:
(the value in var searchValue in this example case will be two)
function myFunction() {
var s = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var url_columnA = 'AAAAA';
var url_vlookup = 'BBBBB';
var dataSearch = Utilities.parseCsv(UrlFetchApp.fetch(url_columnA));
var dataList = dataSearch.join("ღ").split("ღ");
var searchValue = s.getRange("Test!A1").getValue();
var index = dataList.indexOf(searchValue);
if (index === -1) {
s.getRange("Test!B1").setValue('off');
} else {
var row = index;
var dataVlookup = Utilities.parseCsv(UrlFetchApp.fetch(url_vlookup));
var foundValue = dataVlookup[row][3];
s.getRange("Test!B1").setValue(foundValue);
}
}
Return example:
other number
var url_vlookup:
Col A
Col B
Col C
Col D
home
1
a
win
away
2
b
loose
one
3
c
number
two
4
d
other number
three
5
e
number again?
var url_columnA:
Col A
home
away
one
two
three
Is there any way to handle var url_vlookup data for search the value in column A so that it's not necessary to use this page var url_columnA separated or is the only way to do it without looping?
The first column can easily be separated after parsing using Array.map:
const dataVlookup = Utilities.parseCsv(UrlFetchApp.fetch(url_vlookup));
const url_columnA = dataVlookup.map(row => row[0])

How to choose a destination spreadsheet of copied data based on spreadsheet name

So I have two tables. Table 1 is a large table with 7 columns. In this table, C2:C has unique ID numbers of people. Table 2 is a smaller table with the names of all people in K3:K and the unique ID on J3:J. Table 2 only has 2 columns, the IDs and the Name. So, All IDs in C2:C exists in K3:K. All the names in K3:K are also the names of sheets in the same worksheet. So what I'm trying to do is:
Loop through the IDs in table 1 (large table) with the IDs in table 2 (small table). If the IDs are the same, then I will copy the whole row in table 1 into my destination sheet. To choose my destination sheet, I check the cell adjacent to the identified ID in table 2 and choose the sheet whose name is the same.
I hope i explained that decently. My problem it's copying things in every 10 intervals. So it copies the 10th row, then the 20th, then the 30th... and I'm confused how to even approach figuring out where I went wrong because I don't understand why it's in 10 interverals and why it's choosing the sheets that its choosing.
If it helps, link to the sheet is: https://docs.google.com/spreadsheets/d/1522hM3mO9AdaTpiS2oI1IK3wwwFXbOz1qHxcMrRzASY/edit?usp=sharing
function copystuff() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheets = SpreadsheetApp.getActiveSpreadsheet().getSheets();
var key = SpreadsheetApp.getActiveSheet().getName();
var currentS = ss.getSheetByName(key);
var sheetNameArray = [];
sheetNameArray = sheets.map(function(sheet){ // puts sheet names into an array
return [sheet.getName()];
});
var name_master = currentS.getRange("K3:K").getValues().flat().filter(r=>r!=''); //key name
var enno_master = currentS.getRange("J3:J").getValues().flat().filter(r=>r!=''); //key ID
var enno_all = currentS.getRange("C2:C").getValues().flat().filter(r=>r!=''); // number of big table
for (x = 0; x< enno_master.length; x++){ //to loop through the key number
for (y = 0; y < enno_all.length; y++){ // to loop through the numbers of big table
if(enno_master[x]==enno_all[y]){ // if ID in column C = name in column J
for(z = 0; z < sheetNameArray.length; z++){ //looping through my sheets
if(name_master[x] == sheetNameArray[[z]]){ //if name in column K, which is of the same row as the key number
var copyrange = currentS.getRange("A"+y+2+":G"+y+2).getValues(); //y is the row in table 1 where the IDs are the same.
var destfile = ss.getSheetByName(sheetNameArray[[z]]); //copying to sheet Z.
destfile.getRange(destfile.getLastRow()+1,1,1,7).setValues(copyrange); //paste
}
}
}}}}
The thing that makes it easy is turning your small table into an object so that when the object is given the number in column 3 of the big table it returns the actual sheet object. The array pA in object sht provides you with the ability to check using indexOf() if the Sheet actually exists in the object without having to use hasOwnProperty().
function copyStuff() {
const ss = SpreadsheetApp.getActive();
const sh = ss.getSheetByName('Sheet1');
const objvs = sh.getRange(3,10,getColumnHeight(10,sh,ss)-2,2).getValues();
const vs = sh.getRange(2,1,sh.getLastRow() - 1,7).getValues();
let sht = {pA:[]};
objvs.forEach(r => {sht[r[0]]=ss.getSheetByName(r[1]); sht.pA.push(r[0])});//This creates an object which correlates enNo to a sheet making it possible to greatly simplify your code.
vs.forEach(r => {
if(~sht.pA.indexOf(r[2])) {//this determines if the sheet is defined in sht object if it is then you can do a copy because the sheet exists
let s = sht[r[2]];
if(s) {
s.appendRow(r);//this appends the entire row to the next empty row at the bottom of data. You could also use copyTo or setValues(). This is a simple approach.
}
}
});
}
Helper function: this function is used to the the height of column j. It's sort of the getLastRow() but only for a column:
function getColumnHeight(col, sh, ss) {
var ss = ss || SpreadsheetApp.getActive();
var sh = sh || ss.getActiveSheet();
var col = col || sh.getActiveCell().getColumn();
var rcA = [];
if (sh.getLastRow()){ rcA = sh.getRange(1, col, sh.getLastRow(), 1).getValues().flat().reverse(); }
let s = 0;
for (let i = 0; i < rcA.length; i++) {
if (rcA[i].toString().length == 0) {
s++;
} else {
break;
}
}
return rcA.length - s;
}
~ Bitwise Not
The easiest way to find information in the documentation is to use the index in it. Especially since they just changed it all and many of us no longer know where everything is.
SUGGESTION:
Since you want to check the large table A:G & then copy each table data that matches every user ID on the second table J:K and move those matched rows to the designated sheet named after the IDs' user names, you can refer to this sample implementation below:
function copystuff() {
var ss = SpreadsheetApp.getActiveSheet();
var main =SpreadsheetApp.getActiveSpreadsheet();
var sheets = SpreadsheetApp.getActiveSpreadsheet().getSheets();
var sheetNameArray = sheets.map(function(sheet){
return [sheet.getName()]});
var lastRowK = ss.getRange(ss.getLastRow(),11).getNextDataCell(SpreadsheetApp.Direction.UP).getRow(); //Get the last row of column K on any worksheet
var user = main.getSheetByName(sheetNameArray[0]).getRange("J3:K"+lastRowK).getValues();
var tableData = main.getSheetByName(sheetNameArray[0]).getRange("A2:G"+main.getLastRow()).getDisplayValues();
var container = [];
for(x=0; x<user.length; x++){ //Loop every user from the second table
for(y=0; y<tableData.length; y++){ //loop through the large table data
if(tableData[y][2] == user[x][0]){ //check each user's ID if it has any matches on the user ID from the large table data
container.push(tableData[y]); //any matched row data from the large table will be placed temporarily on this container
}
}
if(container.length != 0){
Logger.log("User "+user[x][1]+ " with ID *"+ user[x][0] +"* has these data:\n"+container);
main.getSheetByName(user[x][1]).getRange(1,1,container.length,7).setValues(container);
}
container = []; //clean container for the next user
}
}
This script will get all table data and place them into an array variables user & tableData where it will be checked and processed to make the script run faster instead of live processing the sheet rows.
Sample Result:
After running the script, here are some of the results:
User name RJ with ID of 42:
User name May with ID of 7:
User name Angelo with ID of 25:
Here's the Log Results for review:

Google App Script: Creating a Unique RefNo, not writing incremented new refno correctly from protected sheet, cannot add leading zero to RefNo

I'm creating a support sheet for an NGO using GAS (Google App Scripting).
I have successfully been able to create a Unique Reference Number with the code below. and storing the incremented number that i use in the Reference Number on a hidden/protected sheet.
The issue is adding atleast 3 leading zero's
Example: Ticket 2 should have a reference T0002
But currently returning T2
Below is my code:
function onSelect(event) {
//This function creates a unique reference number
//When a new support item is logged
//It then stores the newest reference number on a locked sheet
//Initialising Googlesheet
var ss = SpreadsheetApp.getActiveSpreadsheet();
var s = event.source.getActiveSheet();
var r = event.source.getActiveRange();
var ws = ss.getSheetByName("Ticket");
var wt = ss.getSheetByName("Ref");
//Retrieves the row number that is being edited
var rownum = r.getRow();
//Initializing the first cell in the row to return its value
var data = ws.getRange(rownum,1).getValues();
//Checks if the data in the cell is empty
if(data == ""){
//When the cell is empty it gets the latest Reference Number
//Execute the GetLatest() Function
var gl = GetLatest();
// Increment the latest Reference Number by 1
var newgl = gl + 1;
//The new Reference number is now stored
wt.getRange(1,1).setValue(newgl);
// Creates the Reference Number that will be written in the Reference column on the Support Log
// Execute NewRef() function
var nr = NewRef();
//Writes the new Reference number in the designated cell
ws.getRange(rownum,1).setValue(nr);
}
}
Created a function separately that fetches the latest reference number stored on the "Ref" sheet.
In cell A1 on the hidden sheet, I have stored value "0001".
function GetLatest(){
// This function fetches the latest reference number stored
//Initialising Googlesheet and cell with the value
var ss = SpreadsheetApp.getActive();
var ws = ss.getSheetByName("Ref");
var row = 1;
var col = 1;
// fetches the value
var value = ws.getRange(row, col).getValue();
return value;
}
Inserts and creates a string for a unique reference number
function NewRef(){
//This function creates the Reference number that the user sees on the Support Log
//Initialising Googlesheet
var ss = SpreadsheetApp.getActive();
var ws = ss.getSheetByName("Ref");
//Fetches latest Reference number
var lr = GetLatest();
//Creates support log reference number
var cnr = "T" + lr;
return cnr;
}
Your lr variable contains the latest reference, if I understand correctly. If you do var cnr = addLeadingZeros(lr) function below on it, you'll be fine. Please run the snippet below and confirm that it works as expected:
const numbers = [7, 12, 34, 321, 4567, 89981];
const numLen = 4;
const addLeadingZeros = num =>
'0'.repeat(numLen - (num + '').length >= 0 ? numLen - (num + '').length : 0) +
(num + '');
numbers.forEach(num => console.log(`${num} becomes ${addLeadingZeros(num)}`));