Google Apps Scripts: Remove data when dates < today + xx days - javascript

I have a Google Sheet with 5 columns and dates of the last check. I want to only keep the entries when the number of days since the last check is > 10 days.
The Sheet: https://docs.google.com/spreadsheets/d/1nD7CXraydrAwOh7q7QFDLveVW76wRNU0ago4h-ORn8U/edit?usp=sharing
function check(){
/** Variables **/
var ss = SpreadsheetApp.getActiveSpreadsheet(); var sh1 = ss.getSheetByName('Data Processing');
/** Remove duplicates **/
var sh1data = sh1.getDataRange().getValues();
var sh1newData = [];
for (var i in sh1data) {
var row = sh1data[i];
var duplicate = false;
for (var j in sh1newData) {
/* DATE */
var today=new Date().valueOf();
var sec=1000; var min=60*sec; var hour=60*min; var day=24*hour; // Do the conversions
var sh1DateChecked = sh1newData[j][4].valueOf();
var diff=today-sh1DateChecked;
var days=Math.floor(diff/day); // Number of Days since the last check
if(row[0] == sh1newData[j][0] && row[1] == sh1newData[j][1] && days < 10)
{ duplicate = true; } }
if (!duplicate) { sh1newData.push(row);
}
}
sh1.clearContents();
sh1.getRange(1, 1, sh1newData.length, sh1newData[0].length).setValues(sh1newData);
}

This solution ignores the code's mention of "duplicates" and focuses on your stated question of removing dates less than 10 days old. However, if you need to remove duplicates as well, it can be easily added.
I propose you put your condition in a function that takes a row of data as input and returns a boolean (true/false) such as
function dateCheckedOlderThan10Days(row) {
return getDays(row[4]) > 10;
}
function getDays(date) {
const sec = 1000;
const min = 60 * sec;
const hour = 60 * min;
const day = 24 * hour;
return Math.floor((new Date() - date)/day);
}
(Note how you can completely extract the getDays function from your main function. That reduces the amount of code you have to think about inside each function definition.)
This function will fit perfectly into Array.prototype.filter, and the naming of the function makes it very clear what you expect to happen.
const dataOlderThan10Days = ss.getSheetByName('Data Processing')
.getDataRange()
.getValues()
.filter(dateCheckedOlderThan10Days);
Here is the checked function refactored into several functions:
Code.js
function check() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const dataSheet = ss.getSheetByName('Data Processing');
const data = dataSheet.getDataRange().getValues();
const dataOlderThan10Days = data
.slice(1) // removes header row; comment out if you don't have a header row
.filter(dateCheckedOlderThan10Days);
dataSheet.clearContents();
//dataOlderThan10Days.unshift(data[0]); // <-- if you want to restore the headers
setData(dataSheet, dataOlderThan10Days);
}
function dateCheckedOlderThan10Days(row) {
return getDays(row[HEADINGS.DATE_CHECKED]) > 10;
}
const HEADINGS = {
FIRST_NAME: 0,
LAST_NAME: 1,
SALARY: 2,
AGE: 3,
DATE_CHECKED: 4
};
function setData(sheet, data) {
sheet.getRange(1, 1, data.length, data[0].length).setValues(data);
}
function getDays(date) {
const sec = 1000;
const min = 60 * sec;
const hour = 60 * min;
const day = 24 * hour;
return Math.floor((new Date() - date)/day);
}

Related

Function works but nothing appears in cell

So I have a function called shift that returns what shift the user is in based on the time.
When I run the program the time and dates go into their cells in my spreadsheet however my shift value does not. I know my shift function returns a value because I've tested it using the test function and the correct value appears in my logs. Here is my code:
const timezone = SpreadsheetApp.getActive().getSpreadsheetTimeZone();
/** #returns {string} */
function timestamp() {
var timestamp_format = "HH:mm:ss";
return Utilities.formatDate(new Date(), timezone, timestamp_format);
}
/** #returns {string} */
function datestamp() {
var datestamp_format = "yyyy-MM-dd";
return Utilities.formatDate(new Date(), timezone, datestamp_format);
}
function shift() {
var shift;
const dt = timestamp();
if(dt > "08:00:00" && dt < "13:59:00"){
shift = "1";
}
return shift;
}
function test(){
shifts = shift();
console.log(shifts);
}
/* #Process Form */
function processFormHood(formObject) {
var url = "GOOGLE DOCS URL";
var ss = SpreadsheetApp.openByUrl(url);
var ws = ss.getSheetByName("SHEETNAME");
ws.appendRow([
datestamp(),
timestamp(),
shift()])
}
The last function takes values from my HTML form and writes it into my spreadsheet. An example would be "formObject.value," I have attempted to do this with my shift function but it did not work. The cell it is supposed to be in gets skipped and everything after it gets filled.
It seems all I had to do was instead of having
function shift() {
var shift;
const dt = timestamp();
if(dt > "08:00:00" && dt < "13:59:00"){
shift = "1";
}
return shift;
}
I needed to do was make it return without the use of varables:
function shift() {
const dt = timestamp();
if(dt > "06:00:00" && dt < "13:59:99"){
return 1;
}
else if(dt > "14:00:00" && dt < "21:59:59"){
return "2,3";
}
}

Javascript interest calculator, how to return values with two different rates

I have this earning calculator function
EarningsCalculator.prototype.computeEarning = function (rate, investedValue) {
var earnings = {};
var currentState = investedValue;
for (var i = 0; i <= 5; i++) {
earning=currentState*rate;
currentState = currentState + earning;
earnings[i] = currentState;
}
return earnings;
}
It takes an "invested value" and based on a give rate, it calculates earning, which in this case is limited to 5 years with a for loop. What I need is to calculate earnings[0] at 1 year interval and earnings[1] through earnings[4] at a 5 year interval.
So with Invested value of 1000 and interest rate of 10%, it returns the following output for 5 years
Year1 = 1100
Year2 = 1210
Year3 = 1331
Year4 = 1464.1
Year5 = 1610.51
What I want is this
Year1 = 1100
Year5 = 1610.51
Year10 = 2593.74246
Year15 = 4177.248169
Year20 = 6727.499949
You could use the formular for calculation the earnings.
function getPeriodicCompounding(p, r, t) {
return Math.pow(r + 1, t) * p;
}
var originalPrincipalSum = 1000,
interestRate = 0.1,
times = [1, 5, 10, 15, 20]
times.forEach(function (time) {
console.log(time, getPeriodicCompounding(originalPrincipalSum, interestRate, time));
});
You could use a modulus on the years in your for loop to add only those years that are divisible by 5. Then add another or condition for the first year:
function EarningsCalculator (rate, investedValue) {
var earnings = {};
var currentState = investedValue;
var YEAR_INTERVAL = 5;
var YEARS_COMPOUNDING = 20;
for (var i = 1; i <= YEARS_COMPOUNDING; i++) {
earning=currentState*rate;
currentState = currentState + earning;
if (i % YEAR_INTERVAL == 0 || i == 1) {
earnings[i] = currentState;
}
}
return earnings;
}
var earnings = [];
earnings = EarningsCalculator(.10,1000);
console.log(earnings);
That should log the correct values to your object in the console.
One advantage of this is being able to change your yearly interval as a variable rather than hardcoding those values in an array for different periods of time.
JSFiddle: https://jsfiddle.net/kgill/zgmrtmhg/1/

Generated nonce length is getting changed

I am trying to generate fixed length nonce (length 9).
But my code is printing sometimes nonce of 8 length and sometime 9 length.
this is what I am trying to do but with different approach (I have modified it for fixed nonce length)
I am not able to understand why it is printing nonce of length 8 when i am passing length as 9 as argument??
It would be great if someone can tell why this is happening.
Below is complete Nodejs code
var last_nonce = null;
var nonce_incr = null;
// if you call new Date to fast it will generate
// the same ms, helper to make sure the nonce is
// truly unique (supports up to 999 calls per ms).
module.exports = {
getNonce: function(length) {
if (length === undefined || !length) {
length = 8;
}
var MOD = Math.pow(10, length);
var now = (+new Date());
if (now !== last_nonce) {
nonce_incr = -1;
}
nonce_incr++;
last_nonce = now;
var nonce_multiplier = ((nonce_incr < 10) ? 10 : ((nonce_incr < 100) ? 100 : 1000));
var s = (((now % MOD) * nonce_multiplier) + nonce_incr) % MOD;
return s;
}
}
//test code
if(require.main === module) {
console.time("run time");
//importing async module
var async = require('async');
var arr = [];
//generating 1000 length array to use it in making 1000 async calls
//to getNonce function
for(var i=0; i<1000; i++) arr.push(i);
//this will call getNonce function 1000 time parallely
async.eachLimit(arr, 1000, function(item, cb) {
console.log(module.exports.getNonce(9));
cb();
}, function(err) {console.timeEnd("run time");});
}
Sample output:
708201864 --> nonce length 9
708201865
708201866
70820190 --> nonce length 8 (why it is coming 8?? when passed length is 9)
70820191
70820192
70820193
70820194
70820195
70820196
70820197
70820198
70820199
708201910
708201911
708201912
708201913
708201914
708201915
708201916
708201917
708201918
In case someone needs it, here is a nonce generator free from convoluted logic, allowing you to control both character sample and nonce size:
const generateNonce = (options) => {
const {
length = 32,
sample = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789',
} = options || {};
const getRand = () => Math.floor(Math.random() * sample.length);
return Array.from({ length }, () => sample.charAt(getRand())).join('');
};
If you prefer Typescript:
const generateNonce = (options?: { sample?: string, length?: number }) => {
const {
length = 32,
sample = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789',
} = options || {};
const getRand = () => Math.floor(Math.random() * sample.length);
return Array.from({ length }, () => sample.charAt(getRand())).join('');
};

Trying to get timer to start once arrived on the page

I made the following fiddle of what I have right now..
https://jsfiddle.net/r5yj99bs/1/
I'm trying to start right when I get onto a page, but allowing the option to leave the pause/resume option. Then is there anyway to display the remaining time as '5 minutes' instead of '300 seconds' and then count down that way rather than only seconds.
<button class="start-pause">Start</button>
<h2 class="time-left"></h2>
var times = [];
var counter_interval;
var $time_left = $('.time-left');
var $button = $('.start-pause');
// timer length in seconds
var timer_length = 300;
$('body').on('click', '.start-pause', function() {
// are we starting or stopping?
var starting = times.length % 2 == 0;
times.push(Date.now());
if (starting) {
$button.html('Pause');
counter_interval = setInterval(function() {
var time_left = Math.floor(timer_length - sum_elapsed());
if (time_left < 1) {
clearInterval(counter_interval);
return finished();
}
$time_left.html(time_left);
}, 100);
} else {
$button.html('Resume');
clearInterval(counter_interval);
}
});
var sum_elapsed = function() {
sum = 0;
for (var i=0; i<times.length; i++) {
if (i % 2 == 1) {
sum += (times[i] - times[i-1]);
}
if (i == (times.length - 1)) {
sum += (Date.now() - times[i]);
}
}
// convert milliseconds to seconds
return sum / 1000;
};
var finished = function() {
$button.attr('disabled','disabled').html('Finished');
$time_left.html("Time's Up");
};
There is a good time module called moment. You can get it through npm or from moments.com
That can format relative time to human readable strings.
If you want to do it yourself, take the seconds modulus 60 to get the minutes. Using modulus you can extract all info about hours and so on.
You may change the following line:
$time_left.html(time_left);
to:
$time_left.html(secToMinTxt(time_left));
and add the following functions:
function pad(num) {
var str = "" + num;
var pad = "00";
return pad.substring(0, pad.length - str.length) + str;
}
function secToMinTxt(seconds) {
var min = Math.floor(seconds / 60);
var sec = seconds % 60;
return pad(min) + ":" + pad(sec);
}
JSFiddle reference : https://jsfiddle.net/r5yj99bs/2/
If interpret Question correctly, try using Math.round with argument existing time_left variable divided by 60
var time_left = Math.round(Math.floor(timer_length - sum_elapsed()) / 60);
jsfiddle https://jsfiddle.net/r5yj99bs/3/

Return 0 if no record in last two hours, javascript logic

I want to return average of records for every two hour for a week. I have start and date time stamp, I returned the calculated average. But I have to check if there is no record for 6 hours then have to return 0 for every tow hours in this case.
What I have done looks like this:
var average = [];
// db query with start-end
db.table.find(query_criteria).sort({"date_time": 1}).toArray(function() {
forEach(function() {
// got record
}
// calculated average for tow hours
average.push(/*average for tow hours*/);
res.send( average );
});
result looks like
average: 2450,
time: "Sun Mar 22 2015 02:00:00",
hour: "2:00"
average: 2780,
time: "Sun Mar 22 2015 04:00:00",
hour: "4:00"
average: 2360,
time: "Sun Mar 22 2015 08:00:00",
hour: "8:00"
Note that there is nothing for 6th hour. I want to check if there is no record between two hours then send 0 average.
Please help to make the logic for this check.
/***********************************************************************
***********************************************************************
** MongoDb
** Will scan MongoDb for weekly temperature average graph.
**
** #author Created by Sajjad
** #date 18/03/15
** #access private
***********************************************************************
***********************************************************************/
var helper = require('../helpers/jsHelper.js');
/**
* Return average of temp from DB
* #param event from service call
* #return
*/
module.exports.temp_graph_by_week = function (event, resp) {
// Selecting table from DB in this case "gateway_status"
var gateway_status = db.collection("gateway_status");
// This will contain records from DB
var temp_record = [];
// This will send average record and other data of device to client side
var temp_list_data = {};
//gateway_id & device_id start date and end date from client side
var gateway_id = parseInt(event.params.gateway_id);
var device_id = parseInt(event.params.device_id);
var start_date = parseInt(event.params.start_date);
var end_date = parseInt(event.params.end_date);
temp_list_data.device_id = device_id;
temp_list_data.gateway_id = gateway_id;
temp_list_data.data = [];
// query criteria
var query_criteria;
if (start_date && end_date != null) {
query_criteria = { "date_time": { "$gte": start_date, "$lte": end_date},
"device_id": device_id, "gateway_id": gateway_id };
// Getting records from DB
gateway_status.find(query_criteria).sort({"date_time": 1}).toArray(function (err, res_data) {
if (err) {
// resp.send(err);
} else {
// will contain indoor temperature average
var temp_average = 0;
// will contain set point average
var setPoint_average = 0;
// sum of indoor temperature
var temp_sum = 0;
// sum of set point temperature
var setPoint_sum = 0;
// for graph weekly bases
var hours_difference = 120;
// Counting the quantity of records
var record_count = 0;
// Date time of first record
var first_time = null;
// Time difference of records
var time_difference = null;
var record_day = '';
var record_check = 0;
res_data.forEach(function (data) {
// Local object
var device_record = {};
if (data.data != null && data.data.length != null) {
var json_data = data.data;
// for indoor temp
for (var i = 0; i < json_data.length; i++) {
if (json_data[i].r == 203) {
//Pass in the data of indoor temp and time
device_record.indoor = json_data[i].v;
device_record.time = data.date_time;
}
// for set point temp
else if (json_data[i].r == 1004) {
//Pass in the data of set point
device_record.setPoint = json_data[i].v;
}
}
}
// Start time
if (first_time == null) {
first_time = new Date(device_record.time);
}
// Time difference of records
time_difference = helper.MinutesBetweenTwoDates(new Date(device_record.time), first_time);
// If we have all records of last two hours
if (time_difference >= hours_difference) {
record_check = 2;
if(new Date(device_record.time).getHours() > record_check){
console.log(new Date(device_record.time).getHours());
}
// Date time of first record
first_time = new Date(device_record.time);
// Calculating average
temp_average = Math.round(temp_sum / record_count);
setPoint_average = Math.round(setPoint_sum / record_count);
var temp_Date = new Date(device_record.time);
temp_Date.setMinutes(0);
temp_Date.setSeconds(0);
temp_Date.setMilliseconds(0);
// console.log(temp_Date,temp_Date.getDay());
if (temp_Date.getDay() == 1) {
record_day = "Monday";
} else if (temp_Date.getDay() == 2) {
record_day = "Tuesday";
} else if (temp_Date.getDay() == 3) {
record_day = "Wednesday";
} else if (temp_Date.getDay() == 4) {
record_day = "Thursday";
} else if (temp_Date.getDay() == 5) {
record_day = "Friday";
} else if (temp_Date.getDay() == 6) {
record_day = "Saturday";
} else if (temp_Date.getDay() == 0) {
record_day = "Sunday";
}
// Push average data of month for sending to client side
temp_list_data.data.push({"indoor": temp_average,
"time": temp_Date.toString(),
"day": record_day,
"hour": temp_Date.getHours().toString() + ":00",
"setPoint": setPoint_average
});
// Set them for next two hours
record_count = 0;
temp_sum = 0;
setPoint_sum = 0;
}
else {
//keep adding values in sum till two hours are filled
temp_sum += device_record.indoor;
setPoint_sum += device_record.setPoint;
//keep counting records
record_count++;
}
});
}
resp.send(temp_list_data);
}
);
}
};
If what I understand is right then you are looking to fill some time slots with some data, and if there is no data at some of these time slots you want to fill them with 0. You can pre fill time slots with 0 and then refill them with the data arriving from db.
//data with record to send
var average_data = [];
//array with time slots
//differ in start date and end date
var array_with_dates = [1,2,3,4,5,6,7,8,9,10];
// Pre fill time slots with 0
for (var j = 0; j < array_with_dates.length; j++){
//required JSON
var data_for_day = {};
data_for_day.day = array_with_dates[i];
//pre fill with 0
data_for_day.average = 0;
//push to the data
average_data.push(data_for_day);
}
//Now refill them with the data from db
db.table.find(query_criteria).sort({"date_time": 1}).toArray(function(err, record) {
record.forEach(function() {
// got record
var record_amount = record.amount;
var current_day = record.day;
//find the right pre filled record and update its avg
for (var i = 0; i < average_data.data.length; i++) {
if (average_data[i].day == current_day) {
average_data[i].ctr++;
average_data[i].sum += (device_record.indoor);
average_data[i].average = Math.round(average_data[i].sum / average_data[i].ctr);
}
}
res.send(average_data);
});

Categories

Resources