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";
}
}
Related
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 1 year ago.
Improve this question
I have an issue with trying to run a script named 'moveRowsFromSpreadsheetToSpreadsheet_ version 2.7' the script will run perfect when he on_edit trigger is installed however when I try to run the script using the on_change trigger it returns an error the sheet "Auto Move Rows Cannot read property 'getlastRow' of undefined code line:267.
Any suggestions will be greatly appreciated on how to get the script to run on_change trigger.
Thanks in advance
/**
* Global variables.
*/
try {
////////////////////////////////
// [START modifiable parameters]
var sheetsToWatch = ['QUOTATIONS'];
var columnsToWatch = ['STATUS', 'STATUS'];
var valuesToWatch = [/^(CONFIRMED)$/i, /^(PENDING)$/i];
var targetSheets = ['DELIVERIES', 'PENDING'];
var targetSpreadheets = ['165PYmS-NkEqM7kSBhZ7EjMF4CIhxvdaIQJB6meJd-DE',
'165PYmS-NkEqM7kSBhZ7EjMF4CIhxvdaIQJB6meJd-DE'];
var targetIdColumn = [];
var targetValues = [];
var transferTypes = ['PASTE_NORMAL'];
var allowArrayFormulas = [true, true];
var copyInsteadOfMove = [true, true];
var numColumnsToMove = [];
var changeColumnOrderTo = [];
var sheetsToSort = ['Target'];
var columnToSortBy = [3];
var sortAscending = [true];
// [END modifiable parameters]
////////////////////////////////
} catch (error) {
showAndThrow_(error);
}
/**
* Moves rows from sheet to sheet, and sorts the source and target sheets after the move.
*
* #param {Object} e The onEdit event object.
*/
function moveRowsAndSortSheet_(e) {
// version 1.1, written by --Hyde, 27 June 2020
// - use LockService
// - sort all targetSheets instead of sorting just one targetSheet
// version 1.0, written by --Hyde, 9 January 2020
// - initial version
try {
var lock = LockService.getDocumentLock();
lock.waitLock(30 * 1000);
var modifiedSheets = moveRowsFromSpreadsheetToSpreadsheet_(e);
if (modifiedSheets) {
[modifiedSheets.sourceSheet].concat(modifiedSheets.targetSheets).forEach(function (sheet) {
sortSheet_(sheet);
});
}
} catch (error) {
showAndThrow_(error);
} finally {
lock.releaseLock();
}
}
/**
* Moves a row from a spreadsheet to another spreadsheet file when a magic value is entered in a column.
*
* The name of the sheet to move the row to is derived from the position of the magic value on the valuesToWatch list.
* The targetSpreadheets list uses spreadsheet IDs that can be obtained from the address bar of the browser.
* Use a spreadsheet ID of '' to indicate that the row is to be moved to another tab in the same spreadsheet file.
*
* Globals: see the Global variables section.
* Displays pop-up messages through Spreadsheet.toast().
* Throws errors.
*
* #param {Object} e The 'on edit', 'on form submit' or 'on change' event object.
* #return {Object} An object that lists the sheets that were modified with this structure, or a falsy value if no rows were moved:
* {Sheet} sourceSheet The sheet from where a row was moved.
* {Sheet[]} targetSheets The sheets to where rows were moved.
* {Number} numRowsMoved The number of rows that were moved to another sheet.
*/
function moveRowsFromSpreadsheetToSpreadsheet_(e) {
var event = getEventObject_(e);
if (!event || sheetsToWatch.indexOf(event.sheetName) === -1) {
return;
}
if (targetIdColumn.length) {
var targetIdColumnNumber = event.columnLabels.indexOf(targetIdColumn[0]) + 1;
if (!targetIdColumnNumber || !targetValues.length) {
throw new Error('Could not find target values in target column "' + String(targetIdColumn[0]) + '".');
}
var valuesInTargetIdColumn = event.sheet.getRange(event.rowStart, targetIdColumnNumber, event.numRows, 1).getDisplayValues();
}
var numRowsMoved = 0;
var messageOnDisplay = false;
var sourceSheetNames = [];
var targetSheetNames = [];
var targets = [];
for (var row = event.numRows - 1; row >= 0; row--) {
for (var column = 0; column < event.numColumns; column++) {
if (event.rowStart + row <= event.columnLabelRow || columnsToWatch.indexOf(event.columnLabels[event.columnStart - 1 + column]) === -1) {
continue;
}
var valuesToWatchIndex = -1;
for (var i = 0, numRegexes = valuesToWatch.length; i < numRegexes; i++) {
if (event.displayValues[row][column].match(valuesToWatch[i])) {
valuesToWatchIndex = i;
break;
}
}
if (valuesToWatchIndex === -1) {
continue;
}
var targetIndex = -1;
if (targetIdColumn.length) {
for (var i = 0, numRegexes = targetValues.length; i < numRegexes; i++) {
if (valuesInTargetIdColumn[row][0].match(targetValues[i])) {
targetIndex = i;
break;
}
}
} else {
targetIndex = valuesToWatchIndex;
}
if (targetIndex === -1) {
continue;
}
if (!messageOnDisplay) {
showMessage_('Moving rows...', 30);
messageOnDisplay = true;
}
var targetSheet = getTargetSheet_(event, targetIndex);
if (!targetSheet) {
continue; // skip moving the row if it would end up on the same sheet
}
var sourceRange = event.sheet.getRange(event.rowStart + row, 1, 1, event.numSheetColumns);
var transferType = transferTypes[targetIndex];
var firstFreeTargetRow = targetSheet.getLastRow() + 1;
if (firstFreeTargetRow > targetSheet.getMaxRows()) {
targetSheet.insertRowAfter(targetSheet.getLastRow());
}
var targetRange = targetSheet.getRange(firstFreeTargetRow, 1);
switch (transferType) {
case 'PASTE_VALUES':
case undefined:
var rowValues = rearrangeRowValues_(sourceRange, targetIndex);
targetSheet.appendRow(rowValues);
break;
case 'PASTE_FORMAT':
// #see https://developers.google.com/apps-script/reference/spreadsheet/copy-paste-type
sourceRange.copyTo(targetRange, SpreadsheetApp.CopyPasteType[transferType], false);
sourceRange.copyTo(targetRange, SpreadsheetApp.CopyPasteType.PASTE_VALUES, false);
break;
case 'PASTE_FORMULA':
case 'PASTE_NORMAL':
case 'PASTE_NO_BORDERS':
sourceRange.copyTo(targetRange, SpreadsheetApp.CopyPasteType[transferType], false);
break;
default:
throw new Error('Unknown transferType "' + transferType + '".');
}
// clear cells in targetRange where column label row contains an array formula
if (allowArrayFormulas[targetIndex]) {
var numColumns = sourceRange.getWidth();
var formulas = targetSheet.getRange(targetSheet.getFrozenRows() || 1, 1, 1, numColumns).getFormulas()[0];
formulas.forEach(function (formula, index) {
if (formula.match(/^=.*arrayformula/i)) {
targetRange.offset(0, index, 1, 1).clearContent();
}
});
}
numRowsMoved += 1;
if (!copyInsteadOfMove[targetIndex]) {
if (event.sheet.getMaxRows() <= event.columnLabelRow + 1) { // avoid deleting the last unfrozen row
event.sheet.appendRow([null]);
}
event.sheet.deleteRow(event.rowStart + row);
}
sourceSheetNames = sourceSheetNames.concat(event.sheetName).filter(filterUniques_);
targetSheetNames = targetSheetNames.concat(targetSheet.getName()).filter(filterUniques_);
targets = targets.concat(targetSheet).filter(filterUniques_);
} // column
} // row
if (messageOnDisplay) {
var message = 'Moved ' + numRowsMoved + (numRowsMoved === 1 ? ' row ' : ' rows ') + "from '" + sourceSheetNames.join(', ') + "' to '" + targetSheetNames.join(', ') + "'.";
showMessage_('Moving rows... done. ' + message);
}
return numRowsMoved ? { sourceSheet: event.sheet, targetSheets: targets, numRowsMoved: numRowsMoved } : null;
}
/**
* Sorts a sheet.
*
* Globals: sheetsToSort, columnToSortBy, sortAscending.
*
* #param {Sheet} sheet The sheet to sort.
*/
function sortSheet_(sheet) {
// version 1.0, written by --Hyde, 19 March 2020
// - initial version
var sheetIndex = sheetsToSort.indexOf(sheet.getName());
if (sheetIndex === -1) {
return;
}
sheet.sort(columnToSortBy[sheetIndex], sortAscending[sheetIndex]);
}
/**
* Determines the type of a spreadsheet event and populates an event object.
*
* Globals: valuesToWatch.
*
* [NOTE] Could add these fields: value, oldValue, values.
*
* #param {Object} e The original event object.
* #return {Object} An event object with the following fields, or null if the event type is unknown.
* {Spreadsheet} spreadsheet The spreadsheet that was edited.
* {Sheet} sheet The sheet that was edited in spreadsheet.
* {Range} range The cell or range that was edited in sheet.
* {String} sheetName The name of the sheet that was edited.
* {Number} rowStart The ordinal number of the first row in range.
* {Number} rowEnd The ordinal number of the last row in range.
* {Number} columnStart The ordinal number of the first column in range.
* {Number} columnEnd The ordinal number of the last column in range.
* {Number} numRows The number of rows in range.
* {Number} numColumns The number of columns in range.
* {String} changeType Always EDIT, and never INSERT_ROW, INSERT_COLUMN, REMOVE_ROW, REMOVE_COLUMN, INSERT_GRID, REMOVE_GRID, FORMAT, or OTHER.
* {String} authMode One of ScriptApp.AuthMode.NONE, .LIMITED, .FULL or .CUSTOM_FUNCTION.
* {String[][]} displayValues The values in range as shown in the spreadsheet as text strings.
* {Number} numSheetColumns The number of columns in sheet.
* {Number} columnLabelRow The 1-based row number where column labels are found.
* {String[]} columnLabels The values in row event.columnLabelRow as shown in the spreadsheet as text strings.
*/
function getEventObject_(e) {
// version 1.1, written by --Hyde, 29 July 2020
// - use Number()
// version 1.0, written by --Hyde, 27 July 2020
// - initial version
if (!e || !valueIsPlainObject_(e)) {
return null;
}
var event = {};
if (e.range && JSON.stringify(e.range) !== '{}') { // triggered by ScriptApp.EventType.ON_EDIT or .ON_FORM_SUBMIT
if (e.value === '') { // optimization for single-cell edits
return null;
}
if (e.value !== undefined) { // optimization for single-cell edits
var valuesToWatchIndex = -1;
for (var i = 0, numRegexes = valuesToWatch.length; i < numRegexes; i++) {
if (e.value.match(valuesToWatch[i])) {
valuesToWatchIndex = i;
break;
}
}
if (valuesToWatchIndex === -1) {
return null;
}
}
event.range = e.range;
event.rowStart = Number(e.range.rowStart);
event.rowEnd = Number(e.range.rowEnd);
event.columnStart = Number(e.range.columnStart);
event.columnEnd = Number(e.range.columnEnd);
event.changeType = 'EDIT';
event.eventType = e.namedValues ? 'ON_FORM_SUBMIT' : 'ON_EDIT';'ON_CHANGE';
} else if (e.changeType === 'EDIT') { // triggered by ScriptApp.EventType.ON_CHANGE
var ss = SpreadsheetApp.getActive();
event.range = ss.getActiveRange();
event.rowStart = event.range.getRow();
event.rowEnd = e.range.getLastRow(1);
event.columnStart = event.range.getColumn();
event.columnEnd = e.range.getLastRow();
event.changeType = e.changeType;
event.eventType = 'ON_CHANGE';
} else { // triggered by some other change type
return null;
}
event.authMode = e.authMode; //
event.displayValues = event.range.getDisplayValues();
event.sheet = event.range.getSheet();
event.sheetName = event.sheet.getName();
event.spreadsheet = event.sheet.getParent();
event.numRows = event.rowEnd - event.rowStart + 1;
event.numColumns = event.columnEnd - event.columnStart + 1;
event.columnLabelRow = event.sheet.getFrozenRows() || 1;
event.numSheetColumns = event.sheet.getLastColumn();
event.columnLabels = event.sheet.getRange(event.columnLabelRow, 1, 1, event.numSheetColumns).getDisplayValues()[0];
return event;
}
function valueIsPlainObject_(value) {
// version 1.0, written by --Hyde, 26 June 2020
// - initial version
var type = typeof value;
if (type === 'function' || Array.isArray(value)) {
return false;
}
return type === 'object' && !!value;
}
function filterUniques_(element, index, array) {
// version 1.0, written by --Hyde, 30 May 2019
// - initial version
return array.indexOf(element) === index;
}
function rearrangeRowValues_(range, targetIndex) {
// version 1.0, written by --Hyde, 27 June 2020
// - initial version, based on inline code in moveRowsFromSpreadsheetToSpreadsheet_
var rowValuesInOriginalOrder = range.getValues()[0];
if (numColumnsToMove[targetIndex] !== undefined) {
var rowValues = rowValuesInOriginalOrder.slice(0, numColumnsToMove[targetIndex]);
} else {
rowValues = rowValuesInOriginalOrder.slice();
}
for (var changeIndex = 0; changeIndex < changeColumnOrderTo.length; changeIndex++) {
if (changeColumnOrderTo[changeIndex] !== undefined) {
rowValues[changeIndex] = rowValuesInOriginalOrder[changeColumnOrderTo[changeIndex]];
} else if (rowValues[changeIndex] === undefined) {
rowValues[changeIndex] = null;
}
}
return rowValues;
}
function getTargetSheet_(event, targetIndex) {
// version 1.0, written by --Hyde, 27 June 2020
// - initial version, based on inline code in moveRowsFromSpreadsheetToSpreadsheet_
var targetSheetName = targetSheets[targetIndex];
var targetSpreadsheetId = String(targetSpreadheets[targetIndex] || '');
if (targetSpreadsheetId) {
try {
var targetSpreadsheet = SpreadsheetApp.openById(targetSpreadsheetId);
} catch (error) {
var ssIdShortened
= targetSpreadsheetId.length > 13
? targetSpreadsheetId.slice(0, 5) + '...' + targetSpreadsheetId.slice(-5)
: targetSpreadsheetId;
throw new Error('Could not open a target spreadsheet with ID "' + ssIdShortened + '".');
}
} else {
targetSpreadsheet = event.spreadsheet;
if (targetSheetName === event.sheetName) {
return null; // skip moving the row if it would end up in the same sheet
}
}
var targetSheet = targetSpreadsheet.getSheetByName(targetSheetName);
if (!targetSheet) {
throw new Error("Could not find the target sheet '" + targetSheetName + "'"
+ targetSpreadsheet === event.spreadsheet
? '.'
: ' in spreadsheet "' + targetSpreadsheet.getName() + '".');
}
return targetSheet;
}
/**
* Installs a trigger that runs each time the user hand edits the spreadsheet.
* Deletes any previous instances of ON_EDIT and ON_CHANGE triggers.
*
* To permanently install the trigger, choose Run > Run function > installOnEditTrigger.
* You only need to install the trigger once per spreadsheet.
* To review the installed triggers, choose Edit > Current project's triggers.
*/
function installOnEditTrigger() {
// version 1.0, written by --Hyde, 7 May 2020
// - initial version
deleteTriggers_(ScriptApp.EventType.ON_EDIT);
deleteTriggers_(ScriptApp.EventType.ON_CHANGE);
ScriptApp.newTrigger('moveRowsAndSortSheet_')
.forSpreadsheet(SpreadsheetApp.getActive())
.onEdit()
.create();
}
/**
* Installs a trigger that runs each time a change is done by Glide, IFTTT, Zapier or other such tools.
* Deletes any previous instances of ON_EDIT and ON_CHANGE triggers.
* [NOTE] This trigger will also move rows when you hand edit the spreadsheet.
* [NOTE] Tested with Glide, but untested with IFTTT, Zapier.
*
* To permanently install the trigger, choose Run > Run function > installOnChangeTrigger.
* You only need to install the trigger once per spreadsheet.
* To review the installed triggers, choose Edit > Current project's triggers.
*/
function installOnChangeTrigger() {
// version 1.0, written by --Hyde, 7 May 2020
// - initial version
deleteTriggers_(ScriptApp.EventType.ON_EDIT);
deleteTriggers_(ScriptApp.EventType.ON_CHANGE);
ScriptApp.newTrigger('moveRowsAndSortSheet_')
.forSpreadsheet(SpreadsheetApp.getActive())
.onChange()
.create();
}
/**
* Deletes all installable triggers of the type triggerType associated with the current
* script project that are owned by the current user in the current spreadsheet.
*
* #param {EventType} triggerType One of ScriptApp.EventType.ON_EDIT, .ON_FORM_SUBMIT, .ON_OPEN, .ON_CHANGE, .CLOCK (time-driven triggers) or .ON_EVENT_UPDATED (Calendar events).
*/
function deleteTriggers_(triggerType) {
// version 1.1, written by --Hyde, 27 June 2020
// - use getUserTriggers(ss) instead of getProjectTriggers()
// version 1.0, written by --Hyde, 7 May 2020
// - initial version
var triggers = ScriptApp.getUserTriggers(SpreadsheetApp.getActive());
for (var i = 0, numTriggers = triggers.length; i < numTriggers; i++) {
if (triggers[i].getEventType() === triggerType) {
ScriptApp.deleteTrigger(triggers[i]);
}
}
}
/**
* Shows error.message in a pop-up and throws the error.
*
* #param {Error} error The error to show and throw.
*/
function showAndThrow_(error) {
// version 1.0, written by --Hyde, 16 April 2020
// - initial version
var stackCodeLines = String(error.stack).match(/\d+:/);
if (stackCodeLines) {
var codeLine = stackCodeLines.join(', ').slice(0, -1);
} else {
codeLine = error.stack;
}
showMessage_(error.message + ' Code line: ' + codeLine, 30);
throw error;
}
/**
* Shows a message in a pop-up.
*
* #param {String} message The message to show.
* #param {Number} timeoutSeconds Optional. The number of seconds before the message goes away. Defaults to 5.
*/
function showMessage_(message, timeoutSeconds) {
// version 1.0, written by --Hyde, 16 April 2020
// - initial version
SpreadsheetApp.getActive().toast(message, 'Auto Move Rows', timeoutSeconds || 5);
}
event.rowEnd = e.range.getLastRow(1); event.columnStart = event.range.getColumn(); event.columnEnd = e.range.getLastRow();
There is no range listed here for an onChange Event.
Also the first getLastRow() has a parameter which is wrong.
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);
}
20.01.2017
23.01.2017
24.01.2017
25.01.2017
26.01.2017
27.01.2017
31.01.2017
01.02.2017
Lets say i have these dates. All those dates are workdays.
Output should be
20.01.2017-27.01.2017
and
31.01.2017-01.02.2017
Since 30 January is workday and so the first period is not continiuing and new period starts.
What is the best way to approach this.
I was thinking taking first date in array and putting it into a new array. Then comparing next date in array to the previous one if it's next day (ignoring weekends). If it is not nextday then take previous date and put it to array as end date and then start a new array of next period.
Get your first date (I'm supossing you have them ordered, as in your example data they are) and store it on a variable for the start date.
Store the same value also in another variable for the end date.
Now loop through your dates checking if current looped date is the next one after your current end date. If it's, store your current looped date into the end date variable and continue to the next loop. If it isn't return current start and end dates and store your currently looped date as a new period start date, go on until loop ends and return current variables.
This would be my approach, though not the shortest or maybe best way of facing this. Just take it as an idea
var array = [
"20.01.2017",
"23.01.2017",
"24.01.2017",
"25.01.2017",
"26.01.2017",
"27.01.2017",
"31.01.2017",
"01.02.2017"
];
var isNextDay = function(day, nextDay) {
var day1 = new Date(day.slice(3, 6) + day.slice(0, 3) + day.slice(6)); //had to format the date this way to make a valid date
day1.setDate(day1.getDate() + 1); //sets the next day, nextday of 30 or 31(last day of month) is 1
var day2 = new Date(nextDay.slice(3, 6) + nextDay.slice(0, 3) + nextDay.slice(6));
if (day1.getTime() === day2.getTime()) {
return true;
} else {
return false;
}
}
var dateGroup = function(dateStrings) {
var res = [];
var aux = dateStrings[0] + "-";
for (var i = 1; i < dateStrings.length; i++) {
if (!isNextDay(dateStrings[i - 1], dateStrings[i])) {
aux += dateStrings[i - 1];
res.push(aux);
aux = dateStrings[i] + "-";
}
}
aux += dateStrings[dateStrings.length - 1];
res.push(aux); //this is because the last one never gets pushed
return res;
}
var output = dateGroup(array);
You can loop over the dates and calculate what the string for the next date should look like, then compare to see if it's the same. If not, end the previous period and start a new one.
You can use a library to parse and format the dates, but simple functions to do the job are just a couple of lines, e.g.
var dates = ['20.01.2017','23.01.2017','24.01.2017',
'25.01.2017','26.01.2017','27.01.2017',
'31.01.2017','01.02.2017'];
/* Parse date in format D/M/YYYY
** #param {string} s - date to parse lke 23.1.2017
** #returns {Date}
*/
function parseDMY(s) {
var b = s.split(/\D/);
return new Date(b[2], b[1]-1, b[0]);
}
/* Format a date in DD/MM/YYYY with supplied separator
** #param {Date} date - date to format
** #param {string} s - separator, default is /
** #returns {string} date formatted as DD/MM/YYYY with supplied separator
*/
function formatDMY(date, s) {
s = s || '/';
function z(n){return (n<10?'0':'')+n}
return [z(date.getDate()),z(date.getMonth()+1),
date.getFullYear()].join(s);
}
/* Create array of date ranges in DD.MM.YYYY-DD.MM.YYYY format
** #param {Array} data - array of date strings in DD.MM.YYYY format
** #returns {Array} array of range strings in DD.MM.YYYY-DD.MM.YYYY format
*/
function createRanges(data) {
var result = [];
var previous;
data.forEach(function(s, i) {
var previous, previousNext, current, range;
// If on first loop, create a range using first value
if (i == 0) {
result.push(s + '-' + s);
// Otherwise, get end date of last range and add one day
} else {
previous = result[result.length-1].split('-')[1];
previousNext = parseDMY(previous);
previousNext.setDate(previousNext.getDate() + 1);
previousNext = formatDMY(previousNext,'.');
// If current date is same as previousNext, update range.
// Otherwise, start a new range
if (s == previousNext) {
range = result[result.length-1];
result[result.length-1] = range.split('-')[0] + '-' + s;
} else {
result.push(s + '-' + s);
}
}
});
// Remove zero day ranges. Could do this by checking last range
// when creating a new one but seems simpler to do it here
result = result.filter(s=>!(s.split('-')[0] == s.split('-')[1]));
return result;
}
console.log(createRanges(dates));
However, a library like moment.js will help with parsing, formatting and arithmetic.
Same as made by Leandro, but made for an array with Date objects and with using moment.js
function groupDates(dates) {
const res = [];
const isNextDay = (day, nextDay) => moment(day).add(1, 'day').isSame(nextDay, 'day');
const format = "DD.MM.YYYY";
let aux = moment(dates[0]).format(format) + "-";
for (let i = 1; i < dates.length; i++) {
if (!isNextDay(dates[i - 1], dates[i])) {
aux += moment(dates[i - 1]).format(format);
res.push(aux);
aux = moment(dates[i]).format(format) + "-";
}
}
aux += moment(dates[dates.length - 1]).format(format);
res.push(aux);
return res;
}
My solution with Luxon lib
const DateTime = luxon.DateTime;
const test = [
"2022-06-23",
"2022-06-24",
"2022-06-25",
"2022-06-26",
"2022-06-27",
"2022-06-28",
"2022-06-29",
"2022-05-02",
"2022-05-03",
"2022-05-05",
"2022-05-04",
"2022-05-06",
"2022-05-07",
"2022-05-08",
];
function getRanges(datesArr) {
const periods = [];
let ix = 0;
const dates = datesArr.map((d) => DateTime.fromSQL(d));
dates.sort();
dates.forEach((date, index) => {
if (index === 0) {
periods.push([
date,
]);
} else if (date.diff(dates[index - 1], [ 'days' ]).days === 1) {
periods[ix].push(date);
} else {
ix += 1;
periods.push([ date ]);
}
})
return periods;
}
console.log(getRanges(test));
<script src="https://cdnjs.cloudflare.com/ajax/libs/luxon/2.4.0/luxon.min.js"></script>
Then you can get first and last elements from each ranges
I am getting 2 errors in IE, that I am not getting in Chrome or Edge.
It could be that it is a compatibility issue with the specific code and IE, or perhaps it is my code itself that is causing the errors.
First error:
Syntax error (no specifics): on this line of code:
var groupedArray = Object.keys(groupedObj).sort().map(key=>groupedObj[key]);
Second error:
Error: [$injector:unpr] Unknown provider: GroupDateRangeServiceProvider <- GroupDateRangeService <- SocialMentionsAnalysisController
Like I mentioned, I do not get these errors in Chrome, nor Edge, but I would prefer to make the site compatible with IE users.
Below is the complete code of my Angular factory/service:
(function () {
'use strict';
angular
.module('portalDashboardApp')
.factory('GroupDateRangeService', GroupDateRangeService);
GroupDateRangeService.$inject = [];
function GroupDateRangeService() {
var service = {
createArray: createArray,
setItemCount: setItemCount,
setSeries: setSeries
};
return service;
function createArray(dateArray, valueArray) {
var arr = dateArray.map(function (s) {
var week = getWeekNumber(parseISOLocal(s));
return week[0] + ('0' + week[1]).slice(-2) + ':' + s;
}).sort();
var results = createGroupedArray(createGroupObject(arr));
if (valueArray && valueArray.length === dateArray.length) {
for (var i = 0; i < results.length; i++) {
results[i] = results[i].map(function (date, index) {
var obj = {};
obj[date] = valueArray[index];
return obj;
});
}
}
var arrayOfWeeks = results;
return results;
}
// Create a Total Count
function setItemCount(dataSet) {
var itemCount = 0;
angular.forEach(dataSet, function (dataItem) {
itemCount = itemCount + dataItem.Total;
});
return itemCount;
};
// Create Chart Series
function setSeries(dataSet, type) {
var series = [{
name: 'Items',
data: [],
showInLegend: false
}];
angular.forEach(dataSet, function (dataItem, dataIndex) {
if (type === 'Count') {
series[0].data[dataIndex] = [dataItem.Description, dataItem.Total];
}
else if (type === 'Percentage') {
series[0].type = 'pie';
series[0].innerSize = '40%';
series[0].data[dataIndex] = [dataItem.Description, dataItem.Percentage];
}
else if (type === 'Adspend') {
series[0].data[dataIndex] = [dataItem.Description, dataItem.Adspend];
}
});
return series;
};
function createGroupObject(arr) {
var groupedObj = arr.reduce(function (result, value) {
var b = value.split(':');
if (!result[b[0]]) result[b[0]] = [];
result[b[0]].push(b[1]);
return result;
}, {});
return groupedObj;
}
function createGroupedArray(groupedObj) {
// Grab arrays in order of week number. Sort keys to maintain order
var groupedArray = Object.keys(groupedObj).sort().map(key=>groupedObj[key]);
// Final set of grouped dates
console.log(groupedArray.join('\n\n'));
return groupedArray;
}
/* Helper to get the ISO week number of a date
** #param {Date} date to get week of
** #returns {Array} [year, weekNumber]
*/
function getWeekNumber(d) {
d = new Date(+d);
d.setHours(0, 0, 0, 0);
d.setDate(d.getDate() + 4 - (d.getDay() || 7));
var yearStart = new Date(d.getFullYear(), 0, 1);
var weekNo = Math.ceil((((d - yearStart) / 86400000) + 1) / 7);
return [d.getFullYear(), weekNo];
}
/* Parse ISO 8601 format date string to local date
** #param {string} s - string to parse like 2016-12-15
** #returns {Date}
*/
function parseISOLocal(s) {
var b = s.split(/\D/);
return new Date(b[0], b[1] - 1, b[2]);
}
}
})();
I would appreciate some guidance!
Here is the solution thanks to zeroflagL:
My issue was the use of ES2015 JavaScript code in my service/factory. This is incomparable with IE, that has not had a new release in 2 years.
So, changing
var groupedArray = Object.keys(groupedObj).sort()
.map(key=>groupedObj[key]);
to:
var groupedArray = Object.keys(groupedObj).sort()
.map(function (key) {
return groupedObj[key];
});
solved both my errors!
I've tried to add a date range search for the jQuery DataTable. The date range filter works fine and the normal Search over all Columns/Rows is also working.
My Problem is at the moment that the refresh of the table (Event) executes only after the Change in the normal search. So i need a Event which repaints / redraws / updates the Table.
So I need a call for something like a refresh at the datatable.
Here is my current code:
window.onload = function () {
$(document).ready(function () {
try {
var table = $('#MainContent_gridClaim').dataTable();
} catch (Err) { };
});
$('.datepicker').pickadate({
selectMonths: true, // Creates a dropdown to control month
selectYears: 15 // Creates a dropdown of 15 years to control year
});
};
$.fn.dataTable.ext.afnFiltering.push(function (settings, data, indx) {
//Min Max Document
var min = document.getElementById("min").value;
var max = document.getElementById("max").value;
if (min === "" || max === "") {
return true;
}
//Res Min Max
var resMin = min.split(".");
var resMax = max.split(".");
//Min Max Date
var dMin = new Date(resMin[2],resMin[1],resMin[0],0,0,0,0);
var dMax = new Date(resMax[2],resMax[1],resMax[0],0,0,0,0);
var resData = data[5].split(".");
var resYear = resData[2].split(" ");
var dJet = new Date(resYear[0], resData[1], resData[0], 0, 0, 0, 0);
var minSec = dMin.getTime();
var maxSec = dMax.getTime();
var actualSec = dJet.getTime();
if (minSec<=actualSec&&actualSec<=maxSec) {
return true;
} else {
return false;
}
});
You need to call
$('#MainContent_gridClaim').dataTable().draw();
when you change the date range. I don't know your date picker plug-in, but you need to add dataTable.draw() call to something like onChange of the datepicker.
There is a similar example https://datatables.net/examples/plug-ins/range_filtering.html