I am new in Apps Script, and had never a chance previously to write in JavaScript.
So, I have a dataset in one sheet in Google about personal information of clients (ID, name, mail, contact). I have another dataset in another Sheet about clients payment (date of payment of the first installment, date of payment of the second installment and amount.
The last sheet that is in focus is the third one relating to unpaid bills.
Now, what I want is to copy columns about personal information from the first sheet into the third one. Here condition would be to copy only information about clients where difference between date of payment of the second installment and today() is greater than 45. Here is what I have tried:
function Neplacena2Novi() {
var ss=SpreadsheetApp.getActiveSpreadsheet();
var sheetFrom=ss.getSheetByName("Podaci o polaznicima"); //Data entry Sheet
var sheetTo=ss.getSheetByName("Neplaceni racuni"); //Data Sheet
var condition=ss.getSheetByName("Uplate novoupisani");
var range1=ss.getRangeByName("Prva rata");
var range2=ss.getRangeByName("Druga rata");
var startRow=3;
var endRow=ss.getLastRow();
function Neplacena2Novi(){
for var(i=startRow; i<(endRow+1)< i++) {
var startDate=new Date(condition.getRange(range1+1).getValue());
var endDate= new Date(condition.getRange(range2+1).getValues());
var difference=parseInt(endDate-startDate)/1000); //Dates are in format dd.mm.yyyy.
if(difference>45){
var values=sheetFrom.getRange(2, 1, sheetFrom.getLastRow(), 4).getValues();
sheetTo.getRange(3,1,1184,4).setValues(values);
}
}
var ui = SpreadsheetApp.getUi();
var potvrda = ui.alert("Odredili ste lica koja treba da se pozovu.");
}
function DugmeDodajKlijenta (){
var ui = SpreadsheetApp.getUi();
}
This code works but I didn't get anything in sheet! And I didn't now how to include today() function in formula for difference. But what I really want is to make a condition where difference would be today()-startDate().
Can someone please help me?
The code is not working because endDate is not a primitive value but a 2D array. Use Range.getValue() instead of Range.getValues(), like this:
const startDate = new Date(condition.getRange(row, startDateColumn).getValue());
const endDate = new Date(condition.getRange(row, endDateColumn).getValue());
...but that is not enough to make the code work. There is a syntactical error in the for loop line, and the i variable is not referenced in the loop. The whole thing is very inefficient because it reads and writes data row by row instead of doing just one read and just one write.
The new Date() calls are superfluous unless the "dates" in the spreadsheet are not numeric dates but text strings that just look like dates. The parseInt() call is also superfluous (plus, it is missing the radix parameter.)
JavaScript dates internally contain the number of milliseconds that have elapsed since 1 January 1970 0:00 UTC. To get the difference between two dates in days, try (endDate-startDate) / 1000 / 60 / 60 / 24. Note that there are many caveats — see Date.
The whole loop could be replaced with Range.getValues().filter(). It looks like the code should best be totally rewritten.
It is unclear why you would need a script in the first place, since the same can be done with a spreadsheet formula using e.g. filter() or query(). The personal info can be added to the missing payments data with a lookup e.g. arrayformula(vlookup()).
Related
I'm relatively new to scripting and I have spent hours getting myself to this point... so any help from here is much appreciated.
I am creating a sheets doc that calculates and generates quotes for a tradie friend of mine.
Each time He calculates a Quote on the Spreedsheet, I want to save all the data + recall it later.
On another occasion, he'll input new values into the cells & want to save all again, with a new quoteID and values, from the same spreadsheet.
Effectively, I need a saved copy of the values that can then be recalled by a particular ID later on if he wants to "regenerate" that quote..
Basically, I have an 3 arrays + 3 single cells that I want to get all values for (nailed this part)
// custom menu function
function onOpen() {
var ui = SpreadsheetApp.getUi();
ui.createMenu('Custom Menu')
.addItem('Save Data','saveData')
.addToUi();
}
// function to save data
function saveData() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var writesheet = ss.getSheetByName("Saved Quotes");
var houselabour = ss.getRange("Calculating the Quote!A7:B38").getValues();
Logger.log(houselabour);
var tradelabour = ss.getRange("Calculating the Quote!E7:F38").getValues();
var materials = ss.getRange("Calculating the Quote!H7:K38").getValues();
var quoteid = ss.getRange('Calculating the Quote!B2').getValue();
var surname = ss.getRange('Calculating the Quote!B1').getValue();
var datecreated = ss.getRange('Calculating the Quote!C2').getValue();
var s1 = houselabour.toString();
var s2 = tradelabour.toString();
var s3 = materials.toString();
writesheet.appendRow([quoteid,surname,datecreated,s1+s2+s3]);
}
//function to recall data
function recallData()
var ss = SpreadsheetApp.getActiveSpreadsheet();
var activequoteid = ss.getActiveCell("Customers").getValue();
//input values from saved data (by quoteid) into Calculating the Quote Spreadsheet... ???
Ideally, I would input these values (quoteid, surname, datecreated) into a new sheet (writesheet) and then i've made the array data into string so i can input it into a single cell (this cell doesn't need to be read, just re-called to fill in the original table later)
Later on, I want to recall the same values I originally extracted back into the original quote generator - "Calculating the Quote" sheet.
2 things: Is it possible, instead of converting the 3 arrays I have to strings, to just log them somewhere without necessarily displaying them in the sheet anywhere? It is essential that the code that runs on a dropdown menu selection "save data" saves the array data and then the next time it is run it does not overrride the original but saves as another
I have a few solutions in my head
1) name each result of the Save Data function something unique (saved as array)...
- i do not know how to do this!
- if i dont print it out anywhere, where does it go???
2) append a row on the save quotes spreadsheet with the string instead of array, then when recalling the data with a new function, convert the object from .getValues
- i also do not know how to do this
... I also feel like there's a super easy solution to this that i'm just completely missing.
Your help is much appreciated
I have created a list of Sentinel 2 images. I have also extracted the date value from the DATATAKE_IDENTIFIER field and pasted it back as a property named "DATE" of ee.Date type for every image of my list. Now i am trying to retrieve the image which is chronologically closest to a date of interest specified by the user.For example if i have dates for: 5-May, 10-May, 15-May, 20-May and the user chooses 14th-May i want to return 15-May. Can someone please help?
Here is my code:
var startDate = ee.Date('2017-07-01');
var finishDate = ee.Date('2017-07-31');
//Create imageCollection with the sentinel2 data and the date filters
var interestImageCollection = ee.ImageCollection(sentinel2_1C_ImageCollection)
.filterBounds(interestRectangle) //changed here your geometrical shape
.filterDate(startDate, finishDate)
.sort('CLOUDY_PIXEL_PERCENTAGE', false);
//function which will extract the date from the 'DATATAKE_IDENTIFIER' field and add it as a new one to every image
var add_date = function(interestImageCollection){
//iterate over the image Collection
//.map must always return something
return interestImageCollection.map(function(image){
//take the image property containing the date
var identifier = ee.String(image.get('DATATAKE_IDENTIFIER'));
//regex
var splitOn = "_|T";
//split
var splitted = identifier.split(splitOn,"");
var date = ee.String(splitted.get(1));
//DATE OPTION
var year = ee.Number.parse(date.slice(0,4));
var month = ee.Number.parse(date.slice(4,6));
var day = ee.Number.parse(date.slice(6,8));
var dateTaken = ee.Date.fromYMD(year, month, day);
return image.set('DATE',dateTaken);
});
};
//list conversion
var subList = interestImageCollection.toList(interestImageCollection.size());
//get the image with the chronologically closest date to my date of interest
var dateOfInterest = ee.Date('2017-07-10');
//failed attempt to use sort for difference in dates calculation and get the closest date
var c = subList.sort(function(a, b){
var distancea = dateOfInterest.difference(a.get['DATE'],'day').round().abs();
var distanceb = dateOfInterest.difference(b.get['DATE'],'day').round().abs();
return distancea - distanceb; // sort a before b when the distance is smaller
}).first();
For programming languages i would use a loop which at every iteration would check if the difference between my desired date and the retrieved date is lower than any previous value and update some temporal variables. But i think that earth engine has a more automated way to do that.
I also tried to use the sort function with a custom comparator but that did not work as well.
Also some regular javascript (like closestTo) functions seem to not be working in earth engine.
I think there is a much shorter and easier solution to this. Each image should have a property named system:time_start that we can use can use to our advantage. Since we know our date, we can add a new property to our images that signify distance to our required date in terms of milliseconds as we can just treat it as numeric operation that going through all the day month considerations that come with YMD format.
ic = ic.map(function(image){
return image.set(
'dateDist',
ee.Number(image.get('system:time_start')).subtract(dateOfInterest.millis()).abs()
);
});
This new field 'dateDist' can now be used to sort the image collection.
ic = ic.sort('dateDist');
The default way of sorting is ascending so this should be good. And you can get the closest scene by taking the first image. One thing to consider is that if your ROI rectangle covers enough area to look at multiple row/path then there might be multiple scenes with closest dates. In such case it might be good idea to inspect how many scenes fall within same date range.
So I am using google sheets with google scripts to sort some data and then put it into a new sheet. I have been using the .valueOf() method to grab the data from an array that grabbed all the data using sheet.getDataRange().getValues(); and has worked perfectly with grabbing everything in all the other places besides my column containing the dates in the "dd/mm/yyyy" order. It grabs it but then puts it into my new sheet in the form of some massive number (which I think could possibly be seconds or milliseconds). I don’t want a time stamp, I want to reverse whatever that time stamp thingy is.
Example: Original Date and After grabbing date and moving to new sheet
Below I have posted the code I am using. Any help or advice is greatly appreciated!
Code: https://pastebin.com/snn3jDcH
line where I grab the date:
for(var i = 1; i < data.length; i++) {
test = data[i][2].valueOf().substring(0,5);
if(test === "ES014")
{
DateStore[count] = data[i][0].valueOf();
NameStore[count] = data[i][1].valueOf();
NumStore[count] = data[i][2].valueOf();
QuantityStore[count] = data[i][3].valueOf();
count++;
}
Sorry about the formatting, but it wouldn't let me post unless I did it this way. No clue why but sorry! Thank you for this long read!
Instead of sheet.getDataRange().getValues(); and then doing valueOf()
Simply only do sheet.getDataRange().getDisplayValues(); and I think valueOf() is not necessary.
For further informations check this out
https://developers.google.com/apps-script/reference/spreadsheet/range#getdisplayvalues
I've been writing a custom function that returns a 365 element array. The problem is that it just doesn't seem to return the values I'd expect. Some of the initial values display, but not others.
The weird thing is that the log files do show the data that I would expect. It's just when I try to output the data to Google sheets that it goes missing.
Which is why I'm wondering if there is some sort of limit on the data that I can return.
A bit more background
My google sheet enables staff to record holiday that they have booked. They enter their holiday in the format Name, StartDate, EndDate.
But I'd also like to show the data in something more like a Gantt chart, with days of the year along the top row and members of staff along the left hand column.
So my function loops through each entry in the holiday table until it finds a matching staff name, then it puts an X in the appropriate days. I call the function again for each staff name until I've fully populated the whole array.
function myFunction(LeaveDates, StaffName, YearStart) {
var ResultsArray = new Array(365);
var MillisecondsInDay = 24*60*60*1000;
for (var i = 0; i < LeaveDates.length; i++) {
if (LeaveDates[i][0] == StaffName) {
for (var j = ((LeaveDates[i][2] - YearStart) / MillisecondsInDay); j < (((LeaveDates[i][3] - YearStart) / MillisecondsInDay)+1); j++) {
ResultsArray[j] = LeaveDates[i][6];
}
Logger.log("Start Date: " + LeaveDates[i][2] + " - End Date: " + LeaveDates[i][3] + " - Type: " + ResultsArray[j-1]);
}
}
return ResultsArray;
}
LeaveDates is the range of cells that contains the holiday data
StaffName is the name of the current member of staff e.g. John Smith
YearStart is the first day of the current year i.e. 01/01/2017
When I call this function from a cell it only populates some of the cells that I'd expect it to. The exact number changes depending on the input data. Sometimes it returns just the first two instances of holiday. Other times it returns the first five. But it's not like it only returns holiday up to a certain date
But the logs show that the data in the array has been populated correctly. So I'm very confused.
Any help is much appreciated!
Thank you everyone who took some time to read my question.
I realised after a good night's sleep that the problem was with my index to the array (j). j was the number for the day of the year (i.e. a number between 0 and 364)
I calculated it by dividing the date by the number of milliseconds in the day. Which works well until the clocks change for daylight savings (British Summer Time). So any entries after the end of March returned array indices that were not whole numbers.
I added Math.round() to my calculation to ensure that I get an integer result and now it works as intended.
I'm a bit surprised that I didn't get any errors when I tried to assign values to non-integer places in the array. Does anyone know of a reason why that is? Are there occasions when you should be able to use non-integer values?
I am building an AngularJS/Rails web app, part of it is creating bookings (function bookings) and in the dashboard I am trying to display two different tabs one with Current Bookings and one with Previous Bookings. the booking model has a function_date attribute and I am retrieving it from the API into a $scope.bookings array.
How to I compare dates (run an if statement on it) to do if function_data > today's date store it into CurrentBookings array if not store it into PreviousBookings array?
Hope that makes sense.
P.S. I am still teaching myself how to program so it might be a stupid question for some.
many way solved this problem but i am using to convert time in milliseconds then easy to compare.
var n = Date.now();
its give the current
Return the number of milliseconds since 1970/01/01:
1460260009358
First, try converting your string into a date. Then compare it to now.
var date = new Date('2030-03-30'); //this is a Date object
if (date > new Date()) { //Date object comparison
//[...]
}
// or
if (date.getTime() > Date.now()) { //unix timestamp (integer) comparison
//[...]
}