Turn array of strings into array of dates in Google Apps Script - arrays

I have a Google Sheets spreadsheet. In Column B, I have a list of strings that are either dates or ranges of dates in the format month/date. For example:
7/26
7/27-7/31
8/1
8/2
8/3-8/5
I want to create an array with the first date on the left and the second date (if any) on the right. If there's no second date, it can be left blank. This is what I want:
[7/26,]
[7/27,7/31]
[8/1,]
[8/2,]
[8/3,8/5]
I've tried:
var r = 'B'
var dateString = sheet.getRange(dateColumns[r] + '1:' + dateColumns[r] + lastRow.toString()).getValues();
var dateArr = Utilities.parseCsv(dateString, '-');
But that just keeps concatenating all values. Also if it's possible to put the output in a date format that would be great too.

This was a funny exercise to play with...
Here is a code that does what you want :
function test(){
convertToDateArray('7/26,7/27-7/31,8/1,8/2,8/3-8/5');
}
function convertToDateArray(inputString){
if(typeof(inputString)=='string'){inputString=inputString.split(',')}; // if input is a string then split it into an array using comma as separator
var data = [];
var datesArray = [];
for(var n in inputString){
if(inputString[n].indexOf('-')==-1){inputString[n]+='-'};// if only 1 field add an empty one
data.push(inputString[n].split('-'));// make it an array
}
Logger.log(data);//check
for(var n in data){
var temp = [];
for(var c in data[n]){
Logger.log('data[n][c] = '+ data[n][c]);
var date = data[n][c]!=''? new Date(2014,Number(data[n][c].split('/')[0])-1,Number(data[n][c].split('/')[1]),0,0,0,0) : '';// create date objects with right values
Logger.log('date = '+date);//check
temp.push(date);
}
datesArray.push(temp);//store output data in an array of arrays, ready to setValues in a SS
}
Logger.log(datesArray);
var sh = SpreadsheetApp.getActive().getActiveSheet();
sh.getRange(1,1,datesArray.length,datesArray[0].length).setValues(datesArray);
}
Logger result for datesArray :
[[Sat Jul 26 00:00:00 GMT+02:00 2014, ], [Sun Jul 27 00:00:00 GMT+02:00 2014, Thu Jul 31 00:00:00 GMT+02:00 2014], [Fri Aug 01 00:00:00 GMT+02:00 2014, ], [Sat Aug 02 00:00:00 GMT+02:00 2014, ], [Sun Aug 03 00:00:00 GMT+02:00 2014, Tue Aug 05 00:00:00 GMT+02:00 2014]]

Related

get Dates In Range in rescript and Daylight Saving Time

i have a calendar in my site which take a start date and end date and pass them into a function who calculates the dates between .
lets sat we have the start date Mon Mar 29 2021 03:00:00 GMT+0300 (Eastern European Summer Time) and the end date is Mon Apr 05 2021 03:00:00 GMT+0300 (Eastern European Summer Time) ; this function should return ["30/3/2021","31/3/2021","1/4/2020","2/4/2020","3/4/2020","4/4/2020"]
let getDatesInRange = (start, end) => {
let dates = ref([])
let current = ref(start)
while current.contents <= end {
dates := dates.contents->Js.Array2.concat([current.contents->toUTCDateString])
current := {
let date = current.contents->toUTCDateString->Js.Date.fromString
date->Js.Date.setDate(date->Js.Date.getDate +. 1.0)->ignore
date
}
}
dates.contents
}
and this is toUTCDateString function which take a date and give the string version of it
let toUTCDateString = date => {
let date = date->External.unSafeCastToJsObject
date["toISOString"]()["split"]("T")[0]
}
These functions where working fine until The time has changed for Daylight Saving Time; we gain an hour so the day stuck there in for some reason
Any body face this issue before and who to deal with such time issues ?

Find unique ID, copy and paste rows to new tab and merge certain rows together if ID is duplicate

I am new to GAS with a little knowledge in Javascript
I am trying to read a list of IDs (column A in 'Outbound' sheet) and paste IDs to new 'temp' sheet (col A) and only show ID once if ID is duplicated, This part of my code is working fine.
Next I want to copy the rows of data over from 'Outbound' sheet to the new 'temp' sheet if ID match, but if a ID is duplicated then it will merge columns E:K.
I haven't got to the merging part as my code is not working when looking through the IDs and pasting the relevant rows across.
Link to Google Sheet and script: Click Here
This is my code so far, I appreciate some variables/lines of codes are not used as I have been playing around with my code and there may be ways to speed things up.
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var newdata = new Array();
var data = ss.getDataRange().getValues(); // get all data
var destSheet = ss.getSheetByName("temp");
var lastRow = sheet.getLastRow();
var lastCol = sheet.getLastColumn();
function main(){
var data = findUnique();
sort();
copyRowData();
}
function findUnique(){
for(nn in data){
var duplicate = false;
for(j in newdata){
if(data[nn][col] == newdata[j][0]){
duplicate = true;
}
}
if(!duplicate){
newdata.push([data[nn][col]]);
}
}
//Logger.log(newdata);
}
function sort(){
newdata.sort(function(x,y){
var xp = Number(x[0]); // ensure you get numbers
var yp = Number(y[0]);
return xp == yp ? 0 : xp < yp ? -1 : 1; // sort on numeric ascending
});
//Logger.log(newdata);
destSheet.clear();
destSheet.getRange(1,1,newdata.length,newdata[0].length).setValues(newdata); // Paste unique HS ID to new tab
}
function copyRowData() {
//var sheet = ss.getSheetByName('Outbound'); //source sheet
var range = sheet.getRange(2,1,lastRow,5).getValues();
Logger.log(range);
var destlastRow = destSheet.getLastRow();
var criteria = destSheet.getRange(1,1,destlastRow).getValues();
Logger.log(criteria);
var data1 = [];
var j =[];
Logger.log(range.length);
//Condition to check in A:A, if true, copy the same row to data array
for (i=0;i<range.length;i++) {
for (j=0; j<criteria.length; j++){
if (range[i] == criteria[j]) {
data1.push(range[i]);
}
}
}
Logger.log(data1.length);
//Copy data array to destination sheet
destSheet.getRange(2,2,data1.length).setValues(data1);
//targetrange.setValues(data1)
}
I am looking for an output similar to this, where Shaun and Kennedy have merged data in cells E to K:
Click for image of expected outcome
Any help is much appreciated.
Modified Script
I approached this a bit differently from your script.
function main() {
let file = SpreadsheetApp.getActive();
let sourceSheet = file.getSheetByName("Outbound");
let sourceRange = sourceSheet.getDataRange();
let sourceValues = sourceRange.getValues();
// Removing header row into its own variable
let headers = sourceValues.shift();
//==========================================
// PHASE 1 - dealing with duplicates
// Initializing the duplicate checking object
// Using the ID as the key, objects will not
// allow duplicate keys.
let data = {}
// For each row in the source
// create another object with a key for each header
// for each key assign an array with the values
sourceValues.forEach(row => {
let rowId = row[0]
// If the id has already been added
if (rowId in data) {
// add the data to the array for each header
headers.forEach((header, index) => {
data[rowId][header].push(row[index]);
})
} else {
// create a new object with an array for each header
// initialize the array with one item
// the value of the cell
let entry = {}
headers.forEach((header, index) => {
entry[header] = [row[index]];
})
data[rowId] = entry
}
})
// PHASE 2 - creating the output
let output = []
// You don't want the name to be merged
// so put the indices of the columns that need to be merged here
let indicesToMerge = [4,5,6,7,9,10]
// For each unique id
for (let id in data) {
// create a row
let newRow = []
// temporary variable of id's content
let entry = data[id]
// for each header
headers.forEach((header, index) => {
// If this field should be merged
if (indicesToMerge.includes(index)) {
// joing all the values with a new line
let content = entry[header].join("\n")
// add to the new row
newRow.push(content)
} else {
// if should not be merged
// take the first value and add to new row
newRow.push(entry[header][0])
}
})
// add the newly constructed row to the output
output.push(newRow)
}
//==========================================
// update the target sheet with the output
let targetSheet = file.getSheetByName("temp");
let targetRange = targetSheet.getRange(
2,1,output.length, output[0].length
)
targetRange.setValues(output)
}
Which outputs this on the temp sheet:
How the script works
This script uses an object to store the data, here would be an example entry after the first phase of the script is done:
'87817':
{
ID: [87817, 87817, 87817],
Name: ["Kennedy", "Kennedy", "Kennedy"],
Surname: ["FFF", "FFF", "FFF"],
Shift: ["NIGHTS", "NIGHTS", "NIGHTS"],
"Area Manager completing initial conversation": ["AM1", "AM1", "AM1"],
"WC Date ": [
Sun Nov 29 2020 19:00:00 GMT-0500 (Eastern Standard Time),
Sun Feb 14 2021 19:00:00 GMT-0500 (Eastern Standard Time),
Sun Mar 07 2021 19:00:00 GMT-0500 (Eastern Standard Time),
],
"Score ": [0.833, 0.821, 0.835],
Comments: ["Comment 6", "Comment 10", "Comment 13"],
"Intial Conversation date": ["Continue to monitor - no action", "", ""],
"Stage 1 Meeting Date": [
"N/A",
Fri Feb 19 2021 19:00:00 GMT-0500 (Eastern Standard Time),
Mon Mar 29 2021 19:00:00 GMT-0400 (Eastern Daylight Time),
],
"Stage 1 Outcome": ["", "Go to Stage 1", "Stage 2"],
};
As you can see, if it finds a duplicate ID, for the first pass, it just copies all the information, including the name and surname etc.
The next phase involves going through each of these entries and merging the headers that need to be merged
by concatenating the results with a newline \n, resulting in a row like this:
[
87817,
"Kennedy",
"FFF",
"NIGHTS",
"AM1\nAM1\nAM1",
"Sun Nov 29 2020 19:00:00 GMT-0500 (Eastern Standard Time)\nSun Feb 14 2021 19:00:00 GMT-0500 (Eastern Standard Time)\nSun Mar 07 2021 19:00:00 GMT-0500 (Eastern Standard Time)",
"0.833\n0.821\n0.835",
"Comment 6\nComment 10\nComment 13",
"Continue to monitor - no action",
"N/A\nFri Feb 19 2021 19:00:00 GMT-0500 (Eastern Standard Time)\nMon Mar 29 2021 19:00:00 GMT-0400 (Eastern Daylight Time)",
"\nGo to Stage 1\nStage 2",
]
Comments
I believe the main difference between this script and yours is that it does everything in memory. That is, it gets all the data, and then never calls getRange or getValues again. Only at the end does it use getRange just for the purposes of outputting to the sheet.
The other difference appears to be that this one uses the inbuilt property of objects to identify duplicates. I.e. an object key cannot be duplicated inside an object.
Perhaps also, this approach takes two passes at the data, because the overhead is minimal and otherwise the code just gets hard to follow.
Merging data like this can get endless as there are many tweaks and checks that can be implemented, but this is a working bare-bones solution that can get you started.

Parsing this format of data into a new one?

I have a format which returns me some dates and I need to parse it into something else which I find a little bit complicated.
The data format is Mon Dec 24 2018 9:00:00 as a startDate for example and Friday Dec 28 2018 17:00:00 as an endTime for example. What happens here is that I select I want someone to start on Monday at 9 until 17 everyday, but what my data does is makes it look like he's working non-stop.
I have tried mapping over it and putting it onto objects with days of the week and start and end times, but I ran into a problem because I create the object like
dates : {
monday: {
start: 9:00,
end: 18:00
},
tuesday: {
start:9:00,
end:18:00
}
// etc for everyday of the week
}
But, what if I only need Monday through Thursday, for example, that would be a problem. Anyone has any idea, how could I do that, in any other way? I was thinking about using moment.js.
I'm not sure if I understand your question, but I think you want to list all days between two different dates, here is an example of function that iterates over days by using moment.isSameOrBefore and moment.add functions, hope this help:
function toDays(startDateString, endDateString) {
const startDate = moment(startDateString, 'dddd MMM DD YYYY');
const endDate = moment(endDateString, 'dddd MMM DD YYYY');
const dates = {};
while(startDate.isSameOrBefore(endDate, 'day')) {
const currentDay = startDate.format('dddd');
dates[currentDay] = {start:'9:00', end:'18:00'};
startDate.add(1, 'days');
}
return dates;
}
const result = toDays('Monday Dec 24 2018', 'Friday Dec 28 2018');
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.23.0/moment.min.js"></script>

changed original date when changed it on local variable

console.log("pre : "+vm.dailyCheckIn);
console.log(vm.temp_date.setHours(0,0,0,0));
console.log("next : "+vm.dailyCheckIn);
can someone help me with this code.
Result:
before temp variable changed (original date value)
pre : Mon Oct 29 2018 16:37:24 GMT+0530 (India Standard Time)
after temp variable changed (original date value)
next : Mon Oct 29 2018 00:00:00 GMT+0530 (India Standard Time)
It seems like that you have used the same date object in the temporary and in the actual variable. You have to create a new date object for the temporary variable.
e.g
var date = new Date();
var vm = {
dailyCheckIn: date,
temp_date: new Date(date) //Create a new date object
};
console.log("pre : "+vm.dailyCheckIn);
console.log(vm.temp_date.setHours(0,0,0,0));
console.log("next : "+vm.dailyCheckIn);
I hope it will help to you.

Date format issue in Firefox browser

My code
for(n in data.values){
data.values[n].snapshot = new Date(data.values[n].snapshot);
data.values[n].value = parseInt(data.values[n].value);
console.log(data.values[n].snapshot);
}
here console.log shows perfect date in Chrome as 'Thu Aug 07 2014 14:29:00 GMT+0530 (India Standard Time)', but in Firefox it is showing as 'Invalid Date'.
If I console.log(data.values[n].snapshot) before the new Date line, it is showing date as
2014-08-07 14:29
How can I convert the date format to Firefox understandable way.
The Date object only officially accepts two formats:
Mon, 25 Dec 1995 13:30:00 GMT
2011-10-10T14:48:00
This means that your date 2014-08-07 14:29 is invalid.
Your date can be easily made compatible with the second date format though (assuming that date is yyyy-mm-dd hh:mm):
for(n in data.values){
n = n.replace(/\s/g, "T");
data.values[n].snapshot = new Date(data.values[n].snapshot);
data.values[n].value = parseInt(data.values[n].value);
console.log(data.values[n].snapshot);
}

Resources