I have a sheet that I have scripted and I am stuck on one spot. The report I get has all of the warehouses together and it's time consuming to manually split them. This is the last part of the script and I am stumped.
Column B is where all of the warehouses are listed. I want all of warehouse 1 to be moved to the page for warehouse 1, warehouse 2 to warehouse 2... etc. This is my code so far and it is not acting as I expect. Calling for orders in warehouse 1 gets me an order from warehouses 2 and 5. Not all of them, just a handful. Any help would be appreciated.
function splitYards(sheet){
//getting the proper range from All Warehouses This works don't change it
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("All Warehouses");
var awLastRow = sheet.getLastRow();
var awRange = sheet.getRange(2,2,awLastRow-1,1);
var awValues = awRange.getValues();
//end of this working
//console.log(awValues);
//begin For statement
for (var i=1; i<awValues.length; i++) {
if (awValues[i] == 1){
var destination = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Warehouse 1");
var drange = sheet.getRange(1,i,1,1);
var dValue = drange.getValues();
//console.log(dValue);
//does not work yet...
// dValue.copyTo(destination);
}
}
}
I have added a simple example to try to explain. Anything that is coming from warehouse 1 gets copied to another sheet that is called Warehouse 1. All items coming from Warehouse 2 gets moved to a sheet called Warehouse 2. I want to move the entire row to the new sheets.
I believe your goal is as follows.
You have a sheet in Google Spreadsheet as shown in your question.
You want to check the column "B" and want to move the rows to the destination sheet.
For example, when the value of column "B" is 1, you want to move the row to the sheet of Warehouse 1 as the appending value.
Modification points:
getValues() returns 2 dimensional array. In this case, awValues[i] == 1 is awValues[i][0] == 1.
In your script, only one sheet is used.
In order to retrieve the values from a row using your for loop, please modify var drange = sheet.getRange(1,i,1,1) to sheet.getRange(i + 2, 1, 1, sheet.getLastColumn()).
For example, when you want to use your for loop, you might be able to modify as follows.
for (var i = awValues.length - 1; i >= 0; i--) {
if (awValues[i][0] == 1) {
var destination = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Warehouse 1");
var drange = sheet.getRange(i + 2, 1, 1, sheet.getLastColumn());
drange.moveTo(destination.getRange(destination.getLastRow() + 1, 1));
}
}
But, when SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Warehouse 1"), moveTo and getValues() is used in a loop, the process cost will be high.
So, in this answer, I would like to propose the following modified script.
Modified script:
function splitYards2() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("All Warehouses");
var range = sheet.getRange("A2:D" + sheet.getLastRow());
var values = range.getValues();
var sheets = [...new Set(values.map(r => r[1]))].reduce((o, e) => (o[e] = ss.getSheetByName("Warehouse " + e) || ss.insertSheet("Warehouse " + e), o), {});
[...values.reduce((m, r) => m.set(r[1], m.has(r[1]) ? [m.get(r[1]), r] : r), new Map())]
.forEach(([k, v]) => {
var s = sheets[k];
if (s) s.getRange(s.getLastRow() + 1, 1, v.length, v[0].length).setValues(v);
});
range.clearContent();
}
When this script is run, the rows are moved from All Warehouses sheet to Warehouse # sheet by checking the column "B".
About some unclear points, I guessed as follows.
I couldn't understand that when the sheet of the sheet name retrieved from the column "B" is not found, what you want to do. So, in this modification, when the sheet name of Warehouse # retrieved from the column "B" is not existing, new sheet is inserted as the sheet name.
References:
reduce()
getRange(row, column, numRows, numColumns)
clearContent()
Your code appears to be grabbing the incorrect range to copy to the destination sheet.
The starting index of awValues is 0. Your code calls sheet.getRange(1,i,1,1), but this fails to consider that the indexes in your array do not align with the row numbers on your sheet. Index 0 of your array actually corresponds to row 2 of your sheet, index 1 -> row 3, and so on.
You should adjust the starting row of your range selection as such:
var drange = sheet.getRange(1,i+2,1,1);
Two additional side notes:
Your iteration of awValues begins at index 1, not 0. This means that you're inadvertently skipping over the first row.
awValues is a two-dimensional array because you called getValues. To avoid confusion, it would be best to treat it as such and change your if condition to awValues[i][0] == 1 (i.e. first column of row i).
Related
I am new to google apps script and I was trying to get all the values in a particular column inside a sheet named "Items". I was able to create a loop to get to the last row that contains value but when I try to use the function, no data is retrieved. I tried console.log(values[lr][0]); inside the if clause and it outputs just fine.
Here's my code
function getAllItems()
{
var ss= SpreadsheetApp.getActiveSpreadsheet();
var locationSheet = ss.getSheetByName("Items");
var values = locationSheet.getRange("Items!B2:B").getValues();
for(var i = values.length - 1 ; i >= 0 ; i--){
if (values[i][0] != null && values[i][0] != ""){
lr = i + 1;
values.sort();
return values[lr][0];
}
}
}
There are several ways to retrieve values from a column in Google Sheets.
The basics, getting the sheet
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var sheet = spreadsheet.getSheetByName('Items');
SpreadsheetApp.getActiveSpreadsheet() works in bounded projects and add-ons. Spreadsheet.getSheetByName(name) works when the sheet name is known.
Getting the column values by using Sheet.getRange and an open reference using A1 notation
var values = sheet.getRange('B:B').getValues();
If your spreadsheet has blank rows at the bottom, in this case Range.getValues besides the column values, it will return an empty string for each blank rows.
Besides using Sheet.getRange with an open reference, it might be used with other reference types and using start row, start column, number of rows and number of columns.
Getting the column values by using Sheet.getRange and an open reference using A1 notation excluding empty strings
var values = sheet.getRange('B:B').getValues().filter(String);
Getting the column values by using Sheet.getDataRange and Array.prototype.map
var values = sheet.getDataRange().getValues().map(row => row[1]);
Only will return the values from the first row to the last row of the data range. The data range is determined from A1 to the last row and last column having values, i.e., if one column B have values from row 1 to row 10 and column C have values from row 4 to row 20, the data range reference is A1:C20, so values will contain the values from row 1 to row 20, showing empty strings for the blank cells.
Getting the column values by using Sheet.getDataRange, Array.prototype.splice and Array.prototype.getLastIndex
var values = sheet.getDataRange().getValues();
values.splice(values.findLastIndex(String) + 1);
Only will return the values from the first row to the last row of the column containing non empty strings. This might be helpful when having columns "of different sizes", as explained in the previous case. Please note that if there blank cells in between, an empty string will be included as value of these cells.
Notes:
Instead of Range.getValues you might use Range.getDisplayValues to get the strings with the values formatted as strings as they are displayed on Google Sheets cells. Both methods return the values structured as an Array of Arrays, this might be handy if you will be adding the values to another range, but if you want to add them to the execution logs you might want to format them in another way.
Please bear in mind that if the column content is very large, nowadays a Google Sheets spreadsheet could have up to 10 million cells and each cell could have upto 50k characters, the column content will be truncated when printed to the execution logs.
Related
Get column from a two dimensional array
Resources
Array
You don't need a loop for that (explanation in comments):
function getAllItems()
{
var ss= SpreadsheetApp.getActiveSpreadsheet();
var locationSheet = ss.getSheetByName("Items");
var values = locationSheet.getRange("Items!B2:B").getValues().flat(); // 2D -> 1D array
var filter_values = values.filter(r=>r!=''); // remove empty rows
Logger.log(filter_values); // get the full list
Logger.log(filter_values[filter_values.length-1]); // get the last value;
return filter_values[filter_values.length-1];
}
Try this:
function getAllItems(){
var ss= SpreadsheetApp.getActive();
var sh = ss.getSheetByName("Items");
var vs = sh.getRange("B2:B"+sh.getLastRow()).getValues();//all the values in column B
return sh.getLastRow();//the last row with data
}
Or you can use:
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;
//const h = Utilities.formatString('col: %s len: %s', col, rcA.length - s);
//Logger.log(h);
//SpreadsheetApp.getUi().showModelessDialog(HtmlService.createHtmlOutput(h).setWidth(150).setHeight(100), 'Col Length')
}
function getAllItems(){
var ss= SpreadsheetApp.getActive();
var sh = ss.getSheetByName("Items");
var vs = sh.getRange("B2:B"+getColumnHeight(2,sh,ss).getValues();//all the values in column B
return sh.getLastRow();//the last row with data
}
If you use filter() to filter out all of the nulls you may not get the desired result if one of the data elements is null.
I'm kind of new to working with Google Sheets, and I've stumbled across an issue while creating buttons for a ticket system. I've been having trouble getting the copied data to just fill the other sheet from the second row and down without gaps or overwriting existing data.
The script basically checks if a checkbox of a certain row is ticked, copies the data in that row, moves that data to another sheet, and then resets that row to it's original state. Meaning it doesn't completely delete the row or format, but only copies the contents.
Here's the code I've been working with so far:
function MigrateInProgress() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var s = ss.getSheetByName('Copy of New Tickets');
var s2 = ss.getSheetByName('In Progress');
var r = s.getRange('G:G');
var v = r.getValues();
var dataset = [];
for(var i=v.length-1;i>=0;i--)
if(v[0,i]=='true') {
var data = s.getRange(i+1,1,1,6).getValues();
s2.getRange(i+1,s2.getLastColumn()-6,data.length,6).setValues(data);
var datasetrange = s.getRange(i+1,1,1,6);
datasetrange.clear({contentsOnly: true});
datasetrange.clear({formatOnly: false});
s.getRange(i+1,7).setValue(false);
}
}
Here's some screenshots to help contextualize what I mean:
Screenshot 1
Screenshot 2
Screenshot 3
Any help would be greatly appreciated. Thank you!
I believe your goal is as follows.
You want to copy the rows of columns "A" to "F" of the source sheet to the destination sheet when the checkbox of column "G" is checked.
At that time, you want to clear the columns "A" to "F" and uncheck the column "G". And, you want to copy the values to the destination sheet without including the empty rows.
In this case, how about the following modification?
Modified script:
function MigrateInProgress() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var s = ss.getSheetByName('Copy of New Tickets');
var s2 = ss.getSheetByName('In Progress');
var v = s.getRange('A2:G' + s.getLastRow()).getValues();
var { values, clear, uncheck } = v.reduce((o, r, i) => {
if (r[6] === true) {
var row = i + 2;
o.values.push(r.splice(0, 6));
o.clear.push(`A${row}:F${row}`);
o.uncheck.push(`G${row}`);
}
return o;
}, { values: [], clear: [], uncheck: [] });
console.log(values)
if (values.length > 0) {
s2.getRange(s2.getLastRow() + 1, 1, values.length, values[0].length).setValues(values);
s.getRangeList(clear).clearContent();
s.getRangeList(uncheck).uncheck();
}
}
In this modification, at first, all values are retrieved from the source sheet. And create the values for copying to the destination sheet, and also create the range list for clearing the cells and unchecking the checkboxes.
By the way, the values retrieved by getValues is 2 dimensional array, and in this case the checkbox returns the boolea type. So when you want to use v[0,i]=='true', you can use v[i][0] === true.
About some unclear points, I guessed as follows.
I couldn't understand whether where you want to put the values when the values are copied to the destination sheet. So in this modification, the values are appended to the destination sheet. When you don't want to append the values, please modify s2.getRange(s2.getLastRow() + 1, 1, values.length, values[0].length).setValues(values); to s2.getRange(2, 1, values.length, values[0].length).setValues(values);. By this, the values are always put from the row 2.
References:
reduce()
clearContent() of Class RangeList
uncheck() of Class RangeList
I'm having an issue with a script for Google sheets
I work in education, and basically we have some teachers who would like to have a form to use when they need to refer a student to an administrator.
So, we have a Google form set up and it's answers populate our "Master" tab within the sheet. Then we have a tab set up for each administrator, the idea being that we can write a function that moves data over based on the contents of the "administrator assigned" box (ColumnT in the code below)
The first bit I need to get functional is the code that handles that part, here's what I've been able to come up with (Heavily commented so that you can hopefully see my thought process):
//Get the active spreadsheet and store in a variable
function importStudName() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
//Get"Master" spreadsheet and store in a variable
var master_sheet = ss.getSheetByName("Master");
//Get "Miranda" spreadhseet and store in a variable
var miranda_sheet = ss.getSheetByName("Miranda");
//Column T
var columnT = master_sheet.getSheetValues(0, 20, 0, -1);
//Column D
var columnD = master_sheet.getSheetValues(0, 4, 0, -1);
//Column E
var columnE = master_sheet.getSheetValues(0, 4, 0, -1);
//Loop through Column T and find every value that = "Miranda"
for(var i = 0; i <= columnT; i++) {
if (columnT[i] === "Miranda") {
//Set a variable to hold the last row in the "Miranda" sheet
var miranda_row = (miranda_sheet.getLastRow() + 1);
//Make the row with the "Miranda" string active
//Set a variable that concatenates the Column D and Column E variables (from "master" sheet) into a string
//Copy the new variable to the "Miranda" sheet
.copyValuesToRange(miranda_sheet, 1, 1, miranda_row, miranda_row);
}
}
}
Mostly, I can't seem to figure out the bit that would make select the row that has the "Miranda" keyword in it. Once I had that down, I think it's just a matter of using the .getRange() function to pull the values from columnD and columnE.
I'm a bit rusty on my JavaScript, and this is the first time I've written something for apps script. So it's possilble I'm going about this all wrong :)
Any suggestions would be welcome
The method getSheetValues returns "Object[][] — a two dimension array of values". So, its entries should be accessed as values[i][j] where i is row offset from the upper left corner of the range, and j is the column offset. In particular, your Miranda comparison should be
if (columnT[i][0] === "Miranda")
Another remark: the row and column numbers in Sheets begin with 1, unlike JavaScript array indices. Passing (0, 20, 0, -1); in getSheetValues indicates you may be miscounting things.
Finally, I'd recommend using .getRange(...).getValues() instead of getSheetValues(). For one thing, you can pass A1 notation to getRange, like
var valuesT = sheet.getRange("T1:T").getValues();
to get all values in column T. If it's preferable to not fetch a bunch of empty cells at the bottom, one can use
var lastRow = sheet.getLastRow();
var valuesT = sheet.getRange("T1:T" + lastRow).getValues();
Sheet 2 has all the items and their statuses, while Sheet 1 has only some of the items from Sheet 2. I want to be able to see every time an item mentioned on Sheet 1 is listed as having a status update, i.e. e date, on Sheet 2.
Here's what I have so far, but having trouble calling the right range to work with. Is there a simpler way to do what I want to do?
function statusupdate() {
var activesht = SpreadsheetApp.getActiveSpreadsheet();
var statussht = activesht.getSheetByName("Sheet 2"); //get sheet on which status update occurs
var statusrng1 = statussht.getRangeByName('B');
var statusrng2 = statussht.getRangeByName('C');
var mainsht = activesht.getSheetByName("Sheet 1"); //get sheet where you show a specific thing has been updated, if that thing mentioned here.
var mainrng = mainsht.getRangeByName('F');
if (statusrng1 == mainrng) {
var date = statusrng2.getValue();
var daterng = mainrng.getRangeByName('E');
daterng.setValues(date);
}
}
Spreadsheet formula
You can have the rows in one sheet follow those in another without using a script. For example, say we have a sheet named Items that contains one row for every item we carry, with the item number in the first column.
We can use VLOOKUP() to search for the row containing info about individual items, and select specific columns from it.
For example, this formula would be used in B2, and could be copied to other cells in our sheet:
=VLOOKUP($A2,Items!$A$2:$C$7,COLUMN(),false)
Script
There are a few issues with your script.
.getRangeByName('B') - This method gets a named range. Given the name, I suspect you mean to get column B, and NOT a named range. If that's the case, you could use this instead:
var statusrng1 = statussht.getRange('B:B');
In A1Notation, the range B:B is the entire column B.
You intend to copy values, so there is another step required beyond identifying ranges; you need to first read the values from a range, and then later write them to a different range. For that, you need to use methods like getValues() and setValues().
Here's an updated version of your script, adapted to the example spreadsheet described above.
function statusupdate() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
//get sheet on which status update occurs
var statusSheet = ss.getSheetByName("Items");
var statusRange = statusSheet.getDataRange();
var statusData = statusRange.getValues();
//get sheet where you show a specific thing has been updated, if that thing mentioned here.
var trackingSheet = ss.getSheetByName("Tracking");
var trackingRange = trackingSheet.getDataRange();
var trackingData = trackingRange.getValues();
// Loop over all rows in the Tracking sheet to update from the Items sheet
// Start with row=1, because row 0 contains headers
for (var row=1; row<trackingData.length; row++) {
var item = trackingData[row][0];
if (item == '') continue; // skip rows without item #
// Look for item in Items sheet
var statusRow = null;
for (var sRow=1; sRow<statusData.length; sRow++) {
if (statusData[sRow][0] == item) {
// Found our match, grab that row
statusRow = statusData[sRow];
break;
}
}
// If we found a matching row, copy the status
if (statusRow) {
// Customize this depending on how your sheets are organized
trackingData[row][1] = statusRow[1];
trackingData[row][2] = statusRow[2];
}
}
// All values have been copied to trackingData, now write to sheet
trackingRange.setValues(trackingData);
}
I've posted a couple questions over the last few days regarding a Google Apps Script Web App I'm working on. So far Serge on here has been super helpful. The post on that script is here.
I'm looking to add a column in that script to generate a uniqueID counter, so that when a new form is submitted, the counter will see previous uniqueID and increment by 1. Idea would be blank spreadsheet with a header row. The script runs on form submit, see A2 is blank and inserts 1. Next submitted form will see 2 in A2 add 1 and put 3 in A3 and so on.
Right now I'm working on just the counter add 1 to the number in the cell. I know as I develop this piece I'll need to grab last row, then find cell value and add to, but beacuse I'm new at GAS, i'm teaching myself as I go, so I"m trying to build slowly to, well you get the idea.
Here is a basic start and I wanted to get some feedback if this is a right direction to start..
function counter() {
//Grabs the range and pinpoint cell
var ssRange = SpreadsheetApp.getActiveSheet().getRange(1,1,1,1);
//Grab the cell value of A1
var value = ssRange.getValue();
//if statement to check value in A1 and if it is null, undefined, false, 0, NAN
if (!value) {
//if the cell is null, undefined, false, 0, NAN sets value to 1
var setCell = ssRange.setValue(1);
}
//if not updates the cell by getting the value and adding 1 to it
else {
var updateCell = ssRange.setValue(value + 1);
}
}
EDIT (updated code to use Phil Bozak tip of .setProperty & .getProperty
I'm still testing on how this code handles if the MYID that inserted into the spreadsheet gets manually changed as you mentioned. I need to ensure the MYID stay unique. Have to think on that.. Here is the code.. Any feedback is appreciated!
function counter2() {
//Get the spreadsheet, range, and value of first cell in range
var sSheet = SpreadsheetApp.getActiveSheet();
var ssRange = sSheet.getRange(1,1,1,1);
var ssValue = ssRange.getValue();
var setUniqueID = ScriptProperties.setProperty("MYID",1);
var getUniqueID = ScriptProperties.getProperty("MYID");
//will set value of first cell to 1 if null, undefined, false, 0, NAN etc.. (runs in if //statement)
var setOne = ssRange.setValue(getUniqueID);
if (!ssValue) {
setOne;
}
else {
//This goes back and get the range, lastrow of range, and value of first cell in range of last row & adds 1 to that value
var setLast = sSheet.getRange(lastRow+1,1,1,1).setValue(getUniqueID + 1);
setLast;
}
}
Another way to be sure to get a value that is always the last count (the highest value) as counter in a column is like that : (comments in code)
I had this idea when you said that values in column A could possibly be modified manually... in this case it is the only approach that avoids possible duplicates (it does not prevent having unused values though... (but this could be implemented too if necessary)
function OnForm(){
var sh = SpreadsheetApp.getActiveSheet()
var startcell = sh.getRange('A1').getValue();
if(! startcell){sh.getRange('A1').setValue(1);return}; // this is only to handle initial situation when A1 is empty.
var colValues = sh.getRange('A1:A').getValues();// get all the values in column A in an array
var max=0;// define the max variable to a minimal value
for(var r in colValues){ // iterate the array
if(Number(colValues[r][0]>max)){max=colValues[r][0]};// get the highest numeric value in th column, no matter what happens in the column... this runs at array level so it is very fast
}
max++ ; // increment to be 1 above max value
sh.getRange(sh.getLastRow()+1, 1).setValue(max);// and write it back to sheet's last row.
}
Ok.. I've updated my code abit and running into an issue I'm hoping someone can help with. The script will see that the spreadsheet is blank and insert a 1 into A1. On next run it will see that 1 is in A1 and add 1 and insert 2 in A2. From then on it just keeps putting 2 in each row over and over.. Here is the code.. not sure why its doing that and not adding 1 each to and increasing the count.
function counter2() {
var sSheet = SpreadsheetApp.getActiveSheet();
var ssRange = sSheet.getRange(1,1,1,1);
var ssValue = ssRange.getValue();
var setOne = ssRange.setValue(1);
var lastRow = sSheet.getLastRow();
var setLast = sSheet.getRange(lastRow+1,1,1,1).setValue(ssValue + 1);
if (!ssValue) {
setOne;
}
else {
setLast;
}
}
EDIT 1
Updated code... The first run on blank spreadsheet fills in A1 = 1 and A2 = 2. It should only put in a 1 in cell A1 and then increment after that. It works fine when running a second time and on. thoughts? What am I missing? function counter2() {
//Get the spreadsheet, range, and value of first cell in range
var sSheet = SpreadsheetApp.getActiveSheet();
var ssRange = sSheet.getRange(1,1,1,1);
var ssValue = ssRange.getValue();
//will set value of first cell to 1 if null, undefined, false, 0, NAN etc.. (runs in if statement)
var setOne = ssRange.setValue(1);
//This goes back and get the range, lastrow of range, and value of first cell in range of last row & adds 1 to that value
var lastRow = sSheet.getLastRow();
var startValue = sSheet.getRange(lastRow,1,1,1).getValue();
var setLast = sSheet.getRange(lastRow+1,1,1,1).setValue(startValue + 1);
if (!ssValue) {
setOne;
}
else {
setLast;
}
}
Edit 2 - Fixed
Here is the updated code that now appears to work fine. I moved the variables "lastRow, startValue, setLast" into the else statement for the second statement. Why would that matter? is it because it runs the variables as it gets to it and by wrapping in the if / else {} brackets it only executes if that statement is run?
function counter2() {
//Get the spreadsheet, range, and value of first cell in range
var sSheet = SpreadsheetApp.getActiveSheet();
var ssRange = sSheet.getRange(1,1,1,1);
var ssValue = ssRange.getValue();
//will set value of first cell to 1 if null, undefined, false, 0, NAN etc.. (runs in if statement)
var setOne = ssRange.setValue(1);
if (!ssValue) {
setOne;
}
else {
//This goes back and get the range, lastrow of range, and value of first cell in range of last row & adds 1 to that value
var lastRow = sSheet.getLastRow();
var startValue = sSheet.getRange(lastRow,1,1,1).getValue();
var setLast = sSheet.getRange(lastRow+1,1,1,1).setValue(startValue + 1);
setLast;
}
}
You can alternatively use ScriptProperties to manage the unique ID. It's a little bit more reliable and a lot easier to work with.
Reliability note: Suppose your spreadsheet somehow was out of order, and your had IDs of [1,2,3,5,4]. Your script would then insert 5 as the next ID. ScriptProperties does a better job of keeping track of the maximum number.
> Sample Usage
var uid = ScriptProperties.getProperty("last_unique_id")+1;
ScriptProperties.setProperty("last_unique_id",uid); //the current UID is now the "last UID"
//do something with the unique_id
You will, of course, have to set the property initially. I recommend you do this with a little function that you run from the menu.
function setUIDOnce() {
ScriptProperties.setProperty("last_unique_id",1);
}