Get months and weeks from range with momentjs - javascript

If I have something like:
let start = moment('2019-01-27');
let end = moment('2019-02-28');
How can I get real months start and end and weeks start and end so I can count some data within those weeks and months.
I have this for weekdays:
getWeekdays(data, labels) {
let start = moment(this.$store.state.labels[0]);
let end = moment(this.$store.state.labels[this.$store.state.labels.length - 1]);
let new_labels = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'];
let store_labels = this.$store.state.labels;
let store_data = this.$store.state.data;
let new_data = [];
for(let i=1; i<8; i++) {
var arr = [];
let tmp = start.clone().day(i);
if( tmp.isAfter(start, 'd') ){
arr.push(tmp.format('YYYY-MM-DD'));
}
while( tmp.isBefore(end) ){
tmp.add(7, 'days');
arr.push(tmp.format('YYYY-MM-DD'));
}
arr.pop();
let count = 0;
_.forEach(arr, function(val) {
let key = store_labels.findIndex(function(i){return i === val});
count = count + store_data[key];
});
new_data.push(count);
}
console.log(new_data);
return {data: new_data, labels: new_labels};
}
Where data = [1,2,5,3,8] and labels = ['2019-01-27','2019-01-28','2019-01-29','2019-01-30','2019-01-31'] and this works, but I am not sure how to do this for months and weeks.

This is answer for months just if someone needs :D
let new_range = {};
_.forEach(labels, function(value, key) {
new_range[moment(value).format('YYYY MM')] = [];
});
_.forEach(labels, function(value, key) {
new_range[moment(value).format('YYYY MM')].push(data[key]);
});
let new_labels = Object.keys(new_range);
let new_data = [];
_.forEach(new_range, function(value, key) {
new_data.push(_.sumBy(value, function(n) {return n;}));
});
return {data: new_data, labels: new_labels};
And after whole day here is one for real weeks:
const range = ['2019-01-22', '2019-01-23', '2019-01-24', '2019-01-25', '2019-01-26', '2019-01-27', '2019-01-28', '2019-01-29', '2019-01-30', '2019-01-31', '2019-02-01', '2019-02-02', '2019-02-03', '2019-02-04', '2019-02-05', '2019-02-06', '2019-02-07', '2019-02-08', '2019-02-09', '2019-02-10', '2019-02-11', '2019-02-12', '2019-02-13', '2019-02-14', '2019-02-15', '2019-02-16', '2019-02-17', '2019-02-18', '2019-02-19', '2019-02-20', '2019-02-21', '2019-02-22', '2019-02-23', '2019-02-24', '2019-02-25', '2019-02-26', '2019-02-27', '2019-02-28', '2019-03-01'];
const backup = ['2019-01-22', '2019-01-23', '2019-01-24', '2019-01-25', '2019-01-26', '2019-01-27', '2019-01-28', '2019-01-29', '2019-01-30', '2019-01-31', '2019-02-01', '2019-02-02', '2019-02-03', '2019-02-04', '2019-02-05', '2019-02-06', '2019-02-07', '2019-02-08', '2019-02-09', '2019-02-10', '2019-02-11', '2019-02-12', '2019-02-13', '2019-02-14', '2019-02-15', '2019-02-16', '2019-02-17', '2019-02-18', '2019-02-19', '2019-02-20', '2019-02-21', '2019-02-22', '2019-02-23', '2019-02-24', '2019-02-25', '2019-02-26', '2019-02-27', '2019-02-28', '2019-03-01'];
const data = [5,2,3,1,5,5,2,3,1,5,5,2,3,1,5,5,2,3,1,5,5,2,3,1,5,5,2,3,1,5,5,2,3,1,5,5,2,3,1];
console.log(bindData(data, backup, getWeeks(range)));
function bindData(data, range, labels) {
let new_data = {};
Object.keys(labels).forEach(function(val) {
let count = 0;
for(let j = 0; j<labels[val].length; j++) {
for(let i = 0; i<range.length; i++) {
if(labels[val][j] == range[i]) {
count = count + data[i];
}
}
}
new_data[val] = count;
});
return new_data;
}
function getWeeks(labels) {
let start = moment(labels[0]);
let new_range = {};
var arr = [];
let tmp = start.clone().day(7);
let sunday = tmp.format('YYYY-MM-DD');
let sunday_index = labels.indexOf(sunday);
if(sunday_index === -1) {
new_range[sunday] = labels;
}else{
let first_week = labels.slice(0, sunday_index + 1);
new_range[first_week[first_week.length - 1]] = first_week;
labels.splice(0, sunday_index + 1);
let last_days = labels.length % 7;
if(last_days != 0) {
let last_week = labels.splice(labels.length - last_days, last_days);
new_range[last_week[last_week.length - 1]] = last_week;
}
for(let i = 0; i<labels.length; i++) {
let a = 0;
new_range[labels[6]] = labels.splice(a, 7);
}
}
return new_range;
}
Now for some reason I need to have double labels array, if I used one from them I would get empty array in bindData function. If you know why. Edit it.

Related

Get dates from array and disable those dates from calendar that repeat more than twice

I want to get dates from array which are repeated 3 times so I can disable those dates from calendar.
function disbaleDate() {
const arr = [
"1/6/2022",
"12/6/2022",
"4/6/2022",
"6/6/2022",
"1/6/2022",
"1/6/2022",
];
const increment = [];
for (let i = 1; i < arr.length; i++) {
for (let j = 0; j < i; j++) {
if (arr[j] === arr[i]) {
increment.push(arr[i]);
}
}
}
console.log(increment);
}
disbaleDate();
const disable = () => {
const arr = [
"1/6/2022",
"12/6/2022",
"4/6/2022",
"6/6/2022",
"1/6/2022",
"1/6/2022",
];
let data =[];
data = arr.filter((el, i) => i !== arr.indexOf(el) )
let result = data.filter((el,i) => i ===data.indexOf(el))
return result;
}
console.log(disable())
Ok, updating my answer
// reducer willproduce an object with the date as the key, and the amount repeated as the value
const countRepeated = arr.reduce((a, c) => {
if (a[c]) {
a[c] = a[c] + 1;
return a;
}
a[c] = 1;
return a;
}, {})
// will filter those whose values are greater than 2
return Object.keys(countRepeated).filter( date => date > 2)
function disableDate() {
const arr = ["1/6/2022", "12/6/2022", "4/6/2022", "6/6/2022", "1/6/2022", "1/6/2022",];
const backendData = ["12/12/2022", "12/6/2021", "14/6/2022", "16/6/2022", "1/6/2022", "11/6/2022",];
const increment = [];
if (backendData.length > 0) {
for (let i = 0; i < backendData.length; i++) {
if (arr.includes(backendData[i])) {
increment.push(backendData[i]);
}
}
}
console.log(increment);
}
disableDate();

How to improve this nested for loop using GAS?

I'd like to learn an alternative way to run this for loop, because currently, it sets the value through each iteration and as this can get large, it better be more performant.
function calculate() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const settingSheet = ss.getSheetByName('settings');
const dataSheet= ss.getSheetByName('Data');
let settingData = settingSheet.getRange(1, 1, settingSheet.getLastRow(), settingSheet.getLastColumn()).getValues();
let spcData = dataSheet.getRange(1, 1, dataSheet.getLastRow(), dataSheet.getLastColumn()).getValues();
let recordTypesAccepted = ['strategy1', 'goal2'];
var settingPar1 = settingData.filter(e => e[0] == 'default').map(e => e[1]);
var settingPar2 = settingData.filter(e => e[0] == 'default').map(e => e[2]);
for (let a = 0; a < spcData.length; a++) {
for (let r = 0; r < settingData .length; r++) {
let settingSearchTerm = settingData[r][4];
let spcSearchTerm = spcData[a][10];
let settingMatchType = settingData[r][5];
let spcMatchType = spcData[a][9];
let spcRecordType = spcData[a][8];
if (recordTypesAccepted.indexOf(spcRecordType) > -1 && settingMatchType === spcMatchType) {
if (settingSearchTerm == spcSearchTerm) {
settingPar1 = settingData[r][7];
settingPar2 = settingData[r][6];
}
let spcClicks = spcData[a][10];
let spcOrders = spcData[a][11];
let row = a + 1;
if (spcClicks >= settingsPar1 && spcOrders >= settingsPar2) {
let newValue = 10;
spcSheet.getRange(row, 20).setValue(newBid).setBackground("#D8E271");
}
}
}
}
}
Thank you!!!
Description
Provided using setValues() doesn't overwrite any formulas you could use getValues() to get an array of the old values, replace any that need to be updated and simply put back the array of values using setValues(). The same for back grounds and number formats.
Note it is assumed from the OP that spcSheet is defined elsewhere.
Script
function calculate() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const settingSheet = ss.getSheetByName('settings');
const dataSheet= ss.getSheetByName('Data');
let settingData = settingSheet.getRange(1, 1, settingSheet.getLastRow(), settingSheet.getLastColumn()).getValues();
let spcData = dataSheet.getRange(1, 1, dataSheet.getLastRow(), dataSheet.getLastColumn()).getValues();
let recordTypesAccepted = ['strategy1', 'goal2'];
var settingPar1 = settingData.filter(e => e[0] == 'default').map(e => e[1]);
var settingPar2 = settingData.filter(e => e[0] == 'default').map(e => e[2]);
let spcRange = spcSheet.getRange(1,20,spcSheet.getLastRow(),1)
var spcValues = spcRange.getValues();
var backgrounds = spcRange.getBackgrounds();
var numberformats = spcRange.getNumberFormats();
for (let a = 0; a < spcData.length; a++) {
for (let r = 0; r < settingData .length; r++) {
let settingSearchTerm = settingData[r][4];
let spcSearchTerm = spcData[a][10];
let settingMatchType = settingData[r][5];
let spcMatchType = spcData[a][9];
let spcRecordType = spcData[a][8];
if (recordTypesAccepted.indexOf(spcRecordType) > -1 && settingMatchType === spcMatchType) {
if (settingSearchTerm == spcSearchTerm) {
settingPar1 = settingData[r][7];
settingPar2 = settingData[r][6];
}
let spcClicks = spcData[a][10];
let spcOrders = spcData[a][11];
if (spcClicks >= settingsPar1 && spcOrders >= settingsPar2) {
let newValue = 10;
spcValues[a][0] = newBid;
backgrounds[a][0] = "#D8E271";
numberformats[a][0] = "€##0.00";
}
}
}
}
spcRange.setValues(spcValues);
spcRange.setBackgrounds(backgrounds);
spcRange.setNumberFormats(numberformats);
}
Reference
Range.getBackgrounds()
Range.setBackgrounds()
Range.getNumberFormats()
Range.setNumberFormats()

Javascript to get the Random sample of data

Hi all,
I am working on a dataset on Google Sheet and I need to get the random data (specified number) by the col 3 names to audit I am using google app script for the same but couldn't able to get the data. Here's the code I tried but I don't want the % of data I want the equally distributed random data for each employee in col 3.
function myFunction() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var trn = ss.getSheetByName('Data');
var originalData = trn.getRange(2, 1, trn.getLastRow() - 1, 3).getValues();
var ReviewerEmail = data;
var data = originalData.filter(function(item) {
return item[1] === 'rob' || item[1] === 'john' || item[1] === 'toger';
});
//Logger.log(data);
var targetsheet = ss.insertSheet(Reviewer);
targetsheet.getRange(1, 1, data.length, data[0].length).setValues(data);
}
function getsampledata() {
var sheet = SpreadsheetApp.getActiveSheet();
var range = sheet.getRange('A:C');
var values = range.getValues();
var headers = values.shift();
var nameColumn = 1;
var uniqueNames = values
.map((row) => row[nameColumn])
.filter((item, i, ar) => ar.indexOf(item) === i)
.filter(String);
var data = [headers];
uniqueNames.forEach(function(name) {
var nameEntries = values.filter((row) => row[nameColumn] == name);
var entries = nameEntries.length;
var tenth = Math.round((entries / 100) * 27.35); //Sampling Percentage
for (i = 0; i < tenth; i++) {
var random = Math.floor(Math.random() * nameEntries.length);
data.push(nameEntries[random]);
nameEntries.splice(random, 1);
}
});
return data;
}
If you want a specific number of sample, then use a parameter to indicate how many you want for each.
Whole working script:
function getsampledata(sample) {
var sheet = SpreadsheetApp.getActiveSheet();
var range = sheet.getRange('A:C');
var values = range.getValues();
var headers = values.shift();
var nameColumn = 2;
var uniqueNames = values
.map((row) => row[nameColumn])
.filter((item, i, ar) => ar.indexOf(item) === i)
.filter(String);
var data = [headers];
uniqueNames.forEach(function(name) {
var nameEntries = values.filter((row) => row[nameColumn] == name);
for (i = 0; i < sample; i++) {
var random = Math.floor(Math.random() * nameEntries.length);
data.push(nameEntries[random]);
nameEntries.splice(random, 1);
}
});
return data;
}

How to merge two arrays of dates and keep the order for other array

I have this code:
var dates1 = ['1/2021', '1/2021', '12/2020'];
var values1 = ['-2500', '-150', '-10000'];
var dates2 = ['2/2021', '3/2021', '1/2021'];
var values2 = ['3000', '1000', '3000'];
What I need is to merge the dates1 with dates2, and keep the same order for values1 and values2 adding a 0 for the dates that has none values, so the result would be this:
var dates = ['12/2020', '1/2021', '2/2021', '3/2021'];
var values1 = ['-10000', '-2650', '0', '0'];
var values2 = ['0', '3000', '3000', '1000'];
To merge the arrays of dates I'm using this code:
var dates = dates1.concat(dates2);
I just don't know how can I keep the same order for values1 and values2 adding a 0 for none values.
Any suggestion ? Thank you !
Break down the algorithm into the smallest steps, then order the steps after each other:
const dates1 = ['1/2021', '1/2021', '12/2020'];
const values1 = ['-2500', '-150', '-10000'];
const dates2 = ['2/2021', '3/2021', '1/2021'];
const values2 = ['3000', '1000', '3000'];
// summing the arrays & keeping track of how
// the values should be ordered
const reduceArr = ({ dates, values }) => {
const reduced = dates.reduce((a, c, i) => {
if (typeof a[c] === 'undefined') a[c] = 0
a[c] += Number(values[i])
return a
}, {})
const filteredDates = [...new Set([...dates])]
const filteredValues = filteredDates.map(date => reduced[date])
return {
filteredDates,
filteredValues,
}
}
// merging the different dates arrays
const mergeDates = ({ dates1, dates2 }) => {
return [...new Set([...dates1, ...dates2])]
}
// time-sorting the merged arrays
const sortDates = ({ dates }) => {
return [...dates].sort((a, b) => {
const [m1, y1] = a.split('/')
const [m2, y2] = b.split('/')
return new Date(y1, m1, 1) - new Date(y2, m2, 1)
})
}
// mapping values based on the orders &
// adding 0 if no value is found
const mapToDates = ({ sortedDates, reducedArr }) => {
return sortedDates.map(date => {
const idx = reducedArr.filteredDates.indexOf(date)
return idx === -1 ? 0 : reducedArr.filteredValues[idx]
})
}
// actually executing the steps:
const mergedDates = mergeDates({ dates1, dates2 })
const sortedDates = sortDates({ dates: mergedDates })
const reducedArr1 = reduceArr({ dates: dates1, values: values1 })
const mapValues1 = mapToDates({ sortedDates, reducedArr: reducedArr1 })
const reducedArr2 = reduceArr({ dates: dates2, values: values2 })
const mapValues2 = mapToDates({ sortedDates, reducedArr: reducedArr2 })
console.log('mapValues1', mapValues1)
console.log('mapValues2', mapValues2)
I think that what you need is that:
Array.prototype.unique = function() {
var a = this.concat();
for(var i=0; i<a.length; ++i) {
for(var j=i+1; j<a.length; ++j) {
if(a[i] === a[j])
a.splice(j--, 1);
}
}
return a;
};
stringToDate = function(str) {
return str.substring(str.search("/")+1, str.search("/")+5) + "-" + (Number(str.substring(0, str.search("/"))) < 10 ? '0' : '') + str.substring(0, str.search("/") ) + "-15T00:00:00Z";
}
dateToString = function(dt) {
dt = new Date(dt);
return (1 + dt.getMonth()) + "/" + dt.getFullYear() ;
}
var dates1 = [stringToDate('1/2021'), stringToDate('1/2021'), stringToDate('12/2020')];
var values1 = ['-2500', '-150', '-10000'];
var dates2 = [stringToDate('2/2021'), stringToDate('3/2021'), stringToDate('1/2021')];
var values2 = ['3000', '1000', '3000'];
var dates_out = dates1.concat(dates2).unique().sort();
var values1_out = new Array(dates_out.length);
var values2_out = new Array(dates_out.length);
dates_out.forEach((dt, i) => {
dates_out[i] = dateToString(dates_out[i]);
values1_out[i] = 0;
values2_out[i] = 0;
dates1.forEach((dt1, i1) => {
if (dt1 === dt) {
if (values1_out[i] != undefined)
values1_out[i] = values1_out[i] + Number(values1[i1]);
else
values1_out[i] = Number(values1[i1]);
}
});
dates2.forEach((dt2, i2) => {
if (dt2 === dt) {
if (values2_out[i] != undefined)
values2_out[i] = values2_out[i] + Number(values2[i2]);
else
values2_out[i] = Number(values2[i2]);
}
});
});
console.log(dates_out);
console.log(values1_out);
console.log(values2_out);
I don't know if this is the best solution. I would create dictionaries to work with the data.
I understood that you need to order the dates (the first result being 12/2020 instead of 1/2021). I also understood that you need the dates as a string, but if you need the date as a datatype, you can remove the part where I convert it back to a string.
here is the solution in python, conversion to javascript should be straight forward. 1. build a list of tuples for dates1/values1 and dates2/values2. 2. Get a list of unique dates. 3. Reduce the tuple lists to a dictionary accumulating on the key which is the date. 4. Using the dates list and the dictionaries create the result value1 and 2 list.
def ReduceToDictionary(tuples):
dict={}
for item in tuples:
key = item[0]
value = item[1]
if key in dict:
dict[key] += float(value)
else:
dict[key] = float(value)
return dict
def BuildList(dates,dict):
result_values=[]
for date in dates:
if date in dict:
val=dict[date]
result_values.append(val)
else:
val=0
result_values.append(val)
return result_values
def StrToDate(string):
groups=re.match('(\d{1,2})[/](\d{4})',string)
year=int(groups[2])
month=int(groups[1])
return(datetime.datetime(year,month,1))
dates1 = ['1/2021', '1/2021', '12/2020']
values1 = ['-2500', '-150', '-10000']
dates2 = ['2/2021', '3/2021', '1/2021']
values2 = ['3000', '1000', '3000']
tuple1=[(StrToDate(dates1[i]),values1[i]) for i in range(len(dates1))]
tuple2=[(StrToDate(dates2[i]),values2[i]) for i in range(len(dates2))]
dates=sorted(set(list(map(StrToDate,dates1))+list(map(StrToDate,dates2))))
dict1=ReduceToDictionary(tuple1)
dict2=ReduceToDictionary(tuple2)
result_values1=BuildList(dates,dict1)
result_values2=BuildList(dates,dict2)
date_string=[date.strftime("%Y/%m") for date in dates]
print(date_string)
print(result_values1)
print(result_values2)
output:
['2020/12', '2021/01', '2021/02', '2021/03']
[-10000.0, -2650.0, 0, 0]
[0, 3000.0, 3000.0, 1000.0]

Count objects (in an array) having timestamps of last few days, weeks and months from 'current time'

I have an array of objects with timestamps as a property:
Sample Input:
const data = [
{
_id: "602102db3acc4515d4b2f687",
createdDt: "2021-02-08T09:22:35.000Z",
},
{
_id: "6021024da706a260d8932da2",
createdDt: "2021-02-08T09:20:13.000Z",
},
// ...
// ...
{
_id: "6020fd863acc4515d4b2f685",
createdDt: "2021-02-08T08:59:50.000Z",
},
];
Now starting from the current date, I need counts of total objects with timestamps of this day, previous day and so on (count can be zero if there is no entry in the previous day).
In the same way, I need counts for this day, previous day and so on, also same with weeks and months.
For example, the output I am expecting could be like:
Sample Output:
const result = {
days: [0, 0, 5, 10, ...],
weeks: [15, 5, 8, 0, ...],
months: [30, 42, 33, 23, ...]
}
I am using ES6, Lodash and moment.js. This is for basic graphical representation.
Update:
This is the code I wrote, can someone suggest a simpler solution?
My current solution:
for (var i = 0, k = 1; i < 365; i++, k++) {
let dt = moment().subtract(i, "days");
let td = moment().subtract(k, "days");
builddays.push(0);
for (var j = 0; j < drivers.length; j++) {
let ddt = new Date(drivers[j].createdDt);
if (moment(ddt).isBetween(td, dt)) builddays[i] = drivers[j].count;
}
}
var weeksbifurcate = builddays.reduce((resultArray, item, index) => {
const chunkIndex = Math.floor(index / 7);
if (!resultArray[chunkIndex]) {
resultArray[chunkIndex] = [];
}
resultArray[chunkIndex].push(item);
return resultArray;
}, []);
var monthsbifurcate = builddays.reduce((resultArray, item, index) => {
const chunkIndex = Math.floor(index / 30);
if (!resultArray[chunkIndex]) {
resultArray[chunkIndex] = [];
}
resultArray[chunkIndex].push(item);
return resultArray;
}, []);
for (i = 0; i < 12; i++) {
days.push(builddays[i]);
weeks.push(weeksbifurcate[i].reduce(getSum, 0));
months.push(monthsbifurcate[i].reduce(getSum, 0));
}
Use moment # diff to find indices, i.e. dayIndex, weekIndex, monthIndex and put or increment the right index in corresponding array in result object:
const solve = (data) => {
const result = {
days: [],
weeks: [],
months: [],
};
data.forEach((item) => {
const today = moment();
const createdDt = moment(item.createdDt);
const dayIndex = today.diff(createdDt, "days");
const weekIndex = today.diff(createdDt, "weeks");
const monthIndex = today.diff(createdDt, "months");
result.days[dayIndex] = (result.days[dayIndex] || 0) + 1;
result.weeks[weekIndex] = (result.weeks[weekIndex] || 0) + 1;
result.months[monthIndex] = (result.months[monthIndex] || 0) + 1;
});
return result;
};
const data = [
{createdDt: "2021-03-03T00:00:00.000Z",},
{createdDt: "2021-03-03T00:00:00.000Z",},
{createdDt: "2021-03-01T00:00:00.000Z",},
{createdDt: "2021-02-28T00:00:00.000Z",},
{createdDt: "2021-02-27T00:00:00.000Z",},
{createdDt: "2021-02-27T00:00:00.000Z",},
{createdDt: "2021-02-27T00:00:00.000Z",},
{createdDt: "2021-02-24T00:00:00.000Z",},
{createdDt: "2021-02-23T00:00:00.000Z",},
];
console.log(solve(data));
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment.min.js"></script>
There might be few undefined values in the result, but there is no need to set it to 0 as it can be considered as 0 wherever you are going to show/use it.

Categories

Resources