Sorting Dates in Custom Google Sheets Calendar - excel

I am trying to populate a calendar with google sheets in the following way :
I have a sheet called processing which has all the projects and their dates (some use one date and some use up until 14 consecutive days). And then I have a sheet called Calendar with the actual calendar to filter all the dates.
I would like to display the project name for every calendar day that it is booked in the processing sheet.
Please see below screenshots
Processing sheet :
Here is the closest to working function I have tried :
=IFERROR(QUERY(Processing!$A$3:$V,"select A where E = date '"&text(H4,"yyyy-mm-dd")&"' or F = date '"&text(H4,"yyyy-mm-dd")&"' or G = date '"&text(H4,"yyyy-mm-dd")&"' or H = date '"&text(H4,"yyyy-mm-dd")&"' or I = date '"&text(H4,"yyyy-mm-dd")&"' or H = date '"&text(H4,"yyyy-mm-dd")&"' or J = date '"&text(H4,"yyyy-mm-dd")&"' or K = date '"&text(H4,"yyyy-mm-dd")&"' or L = date '"&text(H4,"yyyy-mm-dd")&"' or M = date '"&text(H4,"yyyy-mm-dd")&"' or N = date '"&text(H4,"yyyy-mm-dd")&"' or O = date '"&text(H4,"yyyy-mm-dd")&"' or P = date '"&text(H4,"yyyy-mm-dd")&"' or Q = date '"&text(H4,"yyyy-mm-dd")&"' or R = date '"&text(H4,"yyyy-mm-dd")&"' or S = date '"&text(H4,"yyyy-mm-dd")&"' or T = date '"&text(H4,"yyyy-mm-dd")&"' or U = date '"&text(H4,"yyyy-mm-dd")&"' or V = date '"&text(H4,"yyyy-mm-dd")&"'",1),"")
So basically on the Calendar I want to reflect/filter all values in Processing sheets column A's value that has a date matching the calendar day - so far it works only for every project's first day - but not all days.
Any help would be greatly appreciated.
Spreadsheet Link
https://docs.google.com/spreadsheets/d/1sowkIl_BcsbPoc9PUMZHL2bM7-XNMql2dV3yJo58-Sc/edit?usp=sharing

You can do the following, using Apps Script:
Store an object based on the data in Processing, in which the keys refer to the different dates, and the values are arrays with the different projects (in the code below, that's called datesAndProjects).
Using the A1 notation where the dates are located in Calendar (that is, B4:K4, B16:K16, B28:K28), loop through the different dates, and for each date, find the corresponding property in datesAndProjects and write the corresponding values (the projects) in that column.
Below is a detailed example of how this can be done (check inline comments for more in-depth explanation).
Code snippet:
// Copyright 2020 Google LLC.
// SPDX-License-Identifier: Apache-2.0
function projectToCalendar() {
const ss = SpreadsheetApp.getActive();
const sourceSheet = ss.getSheetByName("Processing");
const sourceData = sourceSheet.getRange(2, 1, sourceSheet.getLastRow()-1, sourceSheet.getLastColumn()).getValues();
let datesAndProjects = {};
sourceData.forEach(row => { // Iterate through each row in "Processing"
const project = row.shift(); // Retrieve first value in each row (project names)
row.forEach(val => {
if (val instanceof Date) { // Check if value is Date
const time = getDayTime(val); // Convert date to milliseconds since Epoch in order to compare them
if (!datesAndProjects[time]) datesAndProjects[time] = []; // Inititalize property if it doesn't exist
datesAndProjects[time].push([project]); // Add project to date
}
});
});
const targetSheet = ss.getSheetByName("Calendar");
const dateRangeNotations = ["B4:K4", "B16:K16", "B28:K28"]; // A1 notations where dates are located
targetSheet.getRangeList(dateRangeNotations).getRanges().forEach(range => { // Iterate through each row with dates
const times = range.getValues()[0].map(date => getDayTime(date)); // Get ms since Epoch in order to compare dates
times.forEach((time, i) => { // Iterate each date (ms)
if (datesAndProjects[time]) { // Check if property exists
targetSheet.getRange(range.getRow() + 2, range.getColumn() + i, datesAndProjects[time].length).setValues(datesAndProjects[time]); // Write values at corresponding column, starting two rows after date cell
}
});
});
}
function getDayTime(date) {
return new Date(date.getFullYear(), date.getMonth(), date.getDate()).getTime();
}
Note:
In this snippet, setValues is being called inside a loop, something which is not optimal for efficiency purposes. This could probably be improved, but I think this snippet is enough for you to get the idea.

Related

Time format in Excel using excel4node

I am trying to print the number of hours in excel sheet using excel4node application. although the number of hours is getting printed in the excel, I am not able to get the sum or average while selecting that column.
Its only counting the columns not calculating the sum of the hours.
I am expecting the average while selecting the columns (Marked in RED color)
But what I am actually getting is counting of columns(Marked in RED color)
ws.cell(i+2,j+2).string(value).style(bodystyle);
value here contains the number of hours.
Ask me for more Clarification.
turn the value into a date object and use setUTCHours & setUTCMinutes
functions to set the desired time.
In the workbook settings set dateFormat attribute to hh:mm .. something like
const ws = new xl.Workbook({ dateFormat: 'hh:mm' });
Use .date() function instead of .string()
Example
const [hours, minutes] = value.split(':');
const date = new Date();
date.setUTCHours(hours);
date.setUTCMinutes(minutes);
-----
const ws = new xl.Workbook({ dateFormat: 'hh:mm' });
-----
ws.cell(i+2,j+2).date(date).style(bodystyle);
There will be a problem though ... this will not work as expected if the sum of the hours is 24 or more.

How to automatically add the ordinal number of an entry based on timstamp to a row when non-contiguous new data is added to it in Google Sheets?

How to make Google sheets automatically add the ordinal number of the current row based on the following criteria 'new entry on this particular row?
How to make it work even when the data is not entered on a row contiguous basis (for example 1st entry on row 1 (say cell B1), 2nd entry on row 2 (in B2), 3rd entry on row 3 (B3), but then 4th entry on row 9 (B9), 5th on row 11 (B11), 6th on row (B7))?
And not adding ordinals to intermediary empty rows (for example when B5/row5, B6/row6, B8/row8, B10/row10 etc. are blanks, don't add ordinals to A5/row5, A6/row6, A8/row8, A10/row10, etc.)?
Here's the expected result:
The Sheet:
Ordinals to non-contiguous rows based on data entry timestamps
So far I've come up with this pull down formula (to be pre-set in A2 and dragged down to A12):
=IF(B2<>"",MAX($A$2:$A)+1,"") [in A2]
=IF(B3<>"",MAX($A$2:$A)+1,"") [in A3]
=IF(B4<>"",MAX($A$2:$A)+1,"") [in A4]
=IF(B5<>"",MAX($A$2:$A)+1,"") [in A5]
=IF(B6<>"",MAX($A$2:$A)+1,"") [in A6]
=IF(B7<>"",MAX($A$2:$A)+1,"") [in A7]
=IF(B8<>"",MAX($A$2:$A)+1,"") [in A8]
=IF(B9<>"",MAX($A$2:$A)+1,"") [in A9]
=IF(B10<>"",MAX($A$2:$A)+1,"") [in A10]
=IF(B11<>"",MAX($A$2:$A)+1,"") [in A11]
=IF(B12<>"",MAX($A$2:$A)+1,"") [in A12]
But the result I get when I enter then data succesively one by one in B2, B3, B4, B7, B9, and B12, is that it updates all previous cells with the new max value so that the succesion isn't achieved.
Here's a gif that shows the live result:
https://i.imgur.com/1I1Dk8f.gifv
How to lock the previous results so that the ordinal count can proceed to the next entry when it occurs consecutively?
I've consulted those post that are close to what I'm looking for:
How do you replace a formula with its result?
using this script:
function freezeOutput(){
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("SummarySheet");
var range = sheet.getRange("A1:A10");
range.copyTo(range, {contentsOnly:true});
}
Stop Excel from updating formula after a criteria is matched
using this formula:
=IF(NOW() < C1,2*A1,A2)
Alternatively this VBA code:
Function EvaluateIf(expression As String, condition As Boolean) As Variant
Application.Volatile
Dim myText As String
Dim myVal As Variant
If condition Then
myVal = Application.Evaluate(expression)
Else
myText = Application.Caller.Text
If IsNumeric(myText) Then
myVal = Val(myText)
Else
myVal = myText
End If
End If
EvaluateIf = myVal
End Function
Function FreezeAfter(expression As String, deadline As Date) As Variant
Application.Volatile
Dim myText As String
Dim myVal As Variant
If Now > deadline Then
myText = Application.Caller.Text
If IsNumeric(myText) Then
myVal = Val(myText)
Else
myVal = myText
End If
Else
myVal = Application.Evaluate(expression)
End If
FreezeAfter = myVal
End Function
How do I copy cell values programatically?
using this script:
function getRangeValues() {
var sheet = SpreadsheetApp.getActiveSheet();
var range = sheet.getRange("A2:B4");
var values = range.getValues();
return values;
};
With Sheet here:
Copy cell values programmatically (72539)
Lastly:
Add the Current Date to a Sheet When Data Is Added So That The Date Does Not Change(Static) – Google Sheets
using this script:
/**
* Creates a Date Stamp if a column is edited.
* https://yagisanatode.com/2018/02/21/add-the-current-date-to-a-sheet-when-data-is-added-so-that-the-date-does-not-changestatic-google-sheets/
*/
//CORE VARIABLES
// The column you want to check if something is entered.
var COLUMNTOCHECK = 6;
// Where you want the date time stamp offset from the input location. [row, column]
var DATETIMELOCATION = [0,-5];
// Sheet you are working on
var SHEETNAME = 'Sheet3'
function onEdit(e) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
//checks that we're on the correct sheet.
if( sheet.getSheetName() == SHEETNAME ) {
var selectedCell = ss.getActiveCell();
//checks the column to ensure it is on the one we want to cause the date to appear.
if( selectedCell.getColumn() == COLUMNTOCHECK) {
var dateTimeCell = selectedCell.offset(DATETIMELOCATION[0],DATETIMELOCATION[8]);
dateTimeCell.setValue(new Date());
}
}
}
But I don't see how to make it work.
The expected result shoud be simply add ordinal number to corresponding cell in column A upon entering data on corresponding cell in column B.
How do we do that?
Flow:
Use edit trigger to capture events in Col B
Get all of Col A on edit of Col B
Calculate Max of Col A and add 1
SetValue the max to Col A
Snippet:
function onEdit(e) {
var rg = e.range,
row = rg.rowStart,
col = rg.columnStart,
sht = rg.getSheet();
//exit code
if (col !== 2 || sht.getName() !== 'Sheet1' || row === 1) return;
//Calculate max value and add 1
rg.offset(0, -1, 1, 1).setValue(//setvalue in colA
rg
.offset(2 - row, -1, sht.getLastRow() - 1, 1) //get all of colA
.getValues()
.reduce(function(acc, curr) {//get max of colA
return Math.max(acc, Number(curr[0]));
}, 0) + 1
);
}
To Read and Practice:
Array#Reduce
Range#Offset
Event Objects

Compare two date fields using SSJS

I have two date fields in a document. I need to check if these two fields contain the same value using SSJS.
The values in those fields can either be a datetime or "not set" (empty)
how can I do that?
Thomas
The easiest way is to compare the String values of the dates:
var d1 = document1.getItemValue("date1").toString();
var d2 = document1.getItemValue("date2").toString();
return d1 == d2
When you want to compare only dates, you can do this by converting the date using SimpleDateFormat java class.
var sdf = new java.text.SimpleDateFormat("YYYY-MM-dd");
var d1 = document1.getItemValueDate("date1");
var d2 = document1.getItemValueDate("date2");
d1 = d1 == null?"":sdf.format(d1);
d2 = d2 == null?"":sdf.format(d2);
return d1.equals(d2)
You can adjust the formatting in the first line to comply to your needs.
More info on formatting can be found here

Excel, find date based on day name in cell

I have table:
I'm trying to find dates of specific day (here: Monday) in specific month in 5 cells below DATE cell.
For example, Mondays in March - 6.03.2017, 13.03.2017, 20.03.2017, 27.03.2017,
I'm "playing" with it for about 2h, trying week.day, mod, choose and anything iC can find in Google but no luck.
Any ideas?
This kind of task can be done more easily by dividing steps. I made an example sheet. When you input some values in yellow cells, output(calculated by formulae) will be displayed in green cells. What you really need is weekday function, plus I used date, join, day, etc.
Of course, this can be done via VBA script or Apps Script, which can be undoubtedly much more elegant. I added the script to the above sheet. The red cells are filled by this custom function. Its logic is almost the same as the flow of the sheet.
function getDaysWithNames(year, month, name) {
name = name.slice(0,3).toUpperCase();
var t;
//get the first day
var day;
for(day=1; day<8; day++) {
t = new Date(year, month-1, day).toDateString().slice(0,3).toUpperCase();
if(t == name) break;
}
//first three days are always valid because they are <= 21
var validDays = [day, day+7, day+14];
//test the fourth day
day += 21;
t = new Date(year, month-1, day).getDate();
if(t == day) validDays.push(day); else return validDays;
//test the fifth day
day += 7;
t = new Date(year, month-1, day).getDate();
if(t == day) validDays.push(day);
return validDays;
}

Making Google sheets for entries made today, this week, and this month

I have a Google sheet that pulls information from a form. The form inputs a timestamp and IDnumber into a spreadsheet. I need to make different sheets to show which of these entries were made Today, This week, and This month.
Example
Timestamp ID
12/1/2012 12345
12/1/2012 55555
12/4/2012 98765
12/15/2012 74823
I need to make a sheet that puts ID 12345 and 55555 into one "DAILY" Sheet, IDs 12345, 55555, and 98765 into a "WEEKLY" sheet, and one that has all of these in a "MONTHLY" sheet.
Here's what I would do...
Add today's date somewhere in the sheet (let's say cell D2) by inputting '=Today()' in D2.
In column C, next to your first two columns, add formulas to calculate the number of days between the date in column A and today's date. In row 2, the formula would look like this...
=DATEDIF(A2,D2,"D")
Then, using the script editor in the spreadsheet, I would write a script that looks at column C and writes values from column A and B depending on that value. I haven't tested the script below, but I think it's what you would need. Essentially, it loops through column C of your results sheet. Depending on the value (days from today), that row is pushed to an array for each range. Then I cleared the sheet for that range before setting the values of the array. I hope this helps!
function daysBetween(){
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet(),
formResultsSheet = spreadsheet.getSheetByName("Form Results"),
daySheet = spreadsheet.getSheetByName("Today"),
weekSheet = spreadsheet.getSheetByName("This Week"),
monthSheet = spreadsheet.getSheetByName("This Month"),
allData = formResultsSheet.getDataRange().getValues(),
dayResults = [],
weekResults = [],
monthResults = [];
for (var i = 0; i < formResultsSheet.getLastRow(); i++) {
if (allData[i][2] === 0){
dayResults.push(allData[i])
} else if (allData[i][2] > 0 && allData[i][2] <= 7) {
weekResults.push(allData[i])
} else if (allData[i][2] > 7 && allData[i][2] <= 30) {
monthResults.push(allData[i])
}
}
daySheet.getDataRange().clear()
daySheet.getRange(1,1,dayResults.length,dayResults[0].length).setValues(dayResults)
weekSheet.getDataRange().clear()
weekSheet.getRange(1,1,weekResults.length,weekResults[0].length).setValues(weekResults)
monthSheet.getDataRange().clear()
monthSheet.getRange(1,1,monthResults.length,monthResults[0].length).setValues(monthResults)
}

Resources