How to check spreadsheet cell value with MATCH without triggering NOW() function? - if-statement

Trying to create a table that checks if the room key is already taken.
Google spreadsheet link.
An employee selects or enters the value into F2 cell. The formula in the G2 cell =IF(ISERROR(MATCH(F2,C4:C350,0)), "Brīvs", "Paņemts") tests whether the key is taken or not. The formula runs through C column and tests for the value.
While matching, it is also triggering the formula in B4 cell. Formula in B4 being =IF(C4>0, now(), "")
Issue is, every time an employee selects/enters the value, the MATCH function triggers the now() function and overrides the newest time if it matches the search criteria.
Is there a way of testing for the value without invoking the now() function so that the time stays as it was? Limiting recalculation counts in spreadsheet settings does not aid since an employee may not enter the value correctly from the 1st time.
Tried putting the value into another cell by "=" to nearby cell and =cell(contents, cell coordinate), but these refer back to the original values and Spreadsheet would recalculate all the references.

Thanks to cOde for providing the hint.
The code below is modified to the following needs. Since the sheets will be subsequently added, the SHEETNAME variable is taken out. I have also added the isBlank option at the end, so that if there is no value in the cell, the timestamp is erased.
/**
* Creates a Date Stamp if a column is edited.
*/
//CORE VARIABLES
// The column you want to check if something is entered.
var COLUMNTOCHECK = 3;
// Where you want the date time stamp offset from the input location. [row, column]
var DATETIMELOCATION = [0,-1];
function onEdit(e) {
SpreadsheetApp.getActiveSpreadsheet();
var ss= SpreadsheetApp.getActiveSheet();
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[1]);
dateTimeCell.setValue(new Date());
}
if (selectedCell.isBlank()) {
dateTimeCell.setValue("");
}
}

Related

DAX Previous Year Normalized Amount

I have a column called "Normalize Data" which has 2 values "yes" and "no".
Requirement:
When the user clicks on "yes", I want my measure(called Prev_YR_Trans) to do one thing, but if the user clicks "no" I want the measure to do another thing.
Let me explain with an example - My measure Prev_YR_Trans displays the transactions for the previous time period. Let's the say current time period is "25", then it will display the transactions for time period "24". Now, if the user clicks on "Yes", then I want the previous year's transactions to get "normalized" i.e. I want it to get multiplied by the variance b/w the 2 time periods.
What I have tried:
Prev_YR_Trans =
#getting the current & previous time period from Table "X"
VAR prevSeason = CALCULATE(MAX('X'[Time Period]))-1
VAR maxSeason = CALCULATE(MAX('X'[Time Period]))
#getting variance b/w prev and current time periods
VAR maxSeason_footfall = CALCULATE(SUM('Y'[Park_Footfall]),'Y'[Time Period]=maxSeason)
VAR prevSeason_footfall = CALCULATE(SUM('Y'[Park_Footfall]),'Y'[Time Period]=prevSeason)
VAR footfall_variance = 1+((maxSeason_footfall-prevSeason_footfall)/prevSeason_footfall)
#trying to get the option that the user clicks on(?)
VAR bb = CALCULATE(('X'[Normalize data]))
#returns normalized numbers if user chooses "Yes" else returns actual numbers
RETURN
IF(bb="Yes",
CALCULATE(SUM('X'[Receipt Count]),'X'[Time Period]= prevSeason)*footfall_variance,
CALCULATE(SUM('X'[Receipt Count]),'X'[Time Period]= prevSeason)
)
In my above measure, the "VAR bb = CALCULATE(('X'[Normalize data]))" is giving me an error as it needs some aggregation like max,min,sum,etc.
How do I resolve my measure so that it displays the correct numbers?
Edit (Solved) More on "Normalize Data" column:
I've solved the "Normalize Data" column by creating a new table called "Normalize Slicer" with yes/no values. Here is the link
Also, if you could help me out with my "Normalize Data" column too then that would great!
So with the "Normalize Data" column - I just want it to display 2 options "Yes" and "No" but I realized I need to create it as a column for the slicer functionality to work.
So for my formula I'd like to display "Yes" if the "Time Period" is less than or equal to the previous time period and "No" otherwise.
I tried to do the following:
Normalize data = IF('X'[Time Period]<=(CALCULATE(MAX('X'[Time Period]))-1),"Yes","No")
But above column just displays "No" for all the values so as a workaround I manually entered the previous time period in the formula:
Normalize data = IF('X'[Time Period]<=24,"Yes","No")
The above formula does work but I'd like it to be dynamic and not a manually entered value.
Insted of
VAR bb = CALCULATE(('X'[Normalize data]))
Use this:
var bb = SELECTEDVALUE('X'[Normalize data])
https://dax.guide/selectedvalue/

Removing item from a list once chosen Dropdown Googlesheets Script

I want to Remove an item from a list once chosen in the dropdown and then the next dropdown won't have that item anymore. I've already made something manually but it takes super long and is not really ideal because you would need to go down in order otherwise it gets all mixed up.
https://docs.google.com/spreadsheets/d/1o3J1ZXzxQL11pdN1FDOQZ7YXbSib6A8KwkxBzO5bSM0/edit?usp=sharing
I've made 3 tabs one where I explain what I want and the other were I made it manually so you have an idea on how it works. But remember the manually one you need to go down in order. I want for it to look at a range so it doesn't matter which dropdown I choose for it to get removed from the list.
If it isn't possible to do it by range then like the same way as manually but then in script form would be a great help as well.
Here is what I tried but I'm really new with scripting so I might not even be close:
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Dropdown');
//get's the value in range A1 ( the firstdropdown value )
var firstDrop = ss.getRange("A1").getValue();
//get's the value in range A3 ( the seconddropdown value )
var secondDrop = ss.getRange("A3").getValue();
//get the list ( I don't want to put the list in here sincec I'll be
//working with entries from google forms. so the list will be changing)
var list = ss.getRange("H1:H5").getValues();
if(firstDrop == list){
var newlist = (list-firstDrop);
}
}
This may work for you - I think it gives you the result you want, but its appearance isn't ideal. I've placed it into your sheet, in the last tab. I believe that this is the same approach as Mateo's earlier answer, but down with a formula instead of code.
=SORT(FILTER(B2:B;NOT(COUNTIF(A2:A;B2:B))))
where A is the column where you are entering data, and B has your list of valid names/words.
I use this formula to filter the list of words/names that show in each dropdown list. It starts with the original list, and excludes any that have already been selected. The data validation for your cells points to the filtered list, not the original list.
The drawback is that once you select a word/name, it is no longer eligible for the dropdown cells, so even the cell where it has already been applied shows it as an invalid entry, with a little red triangle. The benefit of a working solution may outweigh this though.
I believe it should look like this:
function availableSlots(){
var form = FormApp.openByUrl('URL_OF_YOUR_FORM'); // TODO add your form url
// need to read what slots are available
var slots = SpreadsheetApp
.getActiveSpreadsheet()
.getRange("slots!A2:C10")
.getValues(); // TODO make sure getRange matches your quota range
var choice = [];
// loop through our available slots
for (s in slots){
// test if slot still available
if (slots[s][0] != "" && slots[s][2] > 0){
choice.push(slots[s][0]); // if so we add to temp array
}
}
var formItems = form.getItems(FormApp.ItemType.LIST); // our form list items
// TODO assumption that first select list is the one you want to change
// change formItems[n] if you have more than one select list
// and we just rewrite all the options to ones that are free
formItems[0].asListItem().setChoiceValues(choice);
}
As I was not sure which dropdown menus you wanted to use I have created a sample sheet with your requirements (photo attached below). In order to reduce the list of items in function of the output of the rest of the dropdowns I have used on onEdit() trigger to check for any changes in these dropdowns and in function of these cell values remove the required items from the list.
The following piece of code has self explanatory comments that address how I have implemented this:
function onEdit(e) {
// Get sheet
var sheet = SpreadsheetApp.getActive().getSheetByName('Share your Ideas here :)');
// Get list from range. Flat will make the 2D array returned by getVaues() into a 1D array
var list = sheet.getRange("E1:E5").getValues().flat();
// Get values from the dropdowns
var a = sheet.getRange("A1");
var b = sheet.getRange("B1");
var c = sheet.getRange("C1");
// Check if any of the values in the dropdowns match the list and if so remove them from
// the list
var index = list.indexOf(a.getValue()); if(index != -1){list.splice(index,1);}
index = list.indexOf(b.getValue()); if(index != -1){list.splice(index,1);}
index = list.indexOf(c.getValue()); if(index != -1){list.splice(index,1);}
// Create the data validation rule with the filtered list
var rule = SpreadsheetApp.newDataValidation().requireValueInList(list).setAllowInvalid(false).build();
// Create/update the dropdowns with the right filtered list
a.setDataValidation(rule);
b.setDataValidation(rule);
c.setDataValidation(rule);
}
References
Data Validation
requireValueInList

How to populate a VLOOKUP with a dynamic range in Google Sheets?

I have a file that needs to look up the team name (on a tab called "Parse") of an employee on a tab called "Roster". This can be done with a simple array Vlookup, however, the issue is that each week, we add a row to the Roster tab because employees sometimes move around.
I have already done the majority of legwork by using a MAX/FILTER/COLUMN, LEN formula to determine what the last column is in the Roster tab. I have also created a range that will provide me with the actual range needed for the VLookup. For example: If the last column in Roster is "G", I already have a formula that populates "'Roster'!A2:G", which is the range I would need for my Vlookup. If the last row in Roster is "P", then the formula shows "'Roster'!A2:P".
The formula that gives me the range, as I described in the examples, lives in cell F2 on the Parse tab.
So the hard part is done. All I need to know now is, when writing my Vlookup, how can I have the formula reference the actual contents of cell F2 in the Parse tab instead of thinking the lookup range is cell F2. Here's a visual:
Cell F2 in the Parse tab reads: 'Roster'!A2:G (because as of now, column G is the last column with data. this is correct).
My VLookup is VLOOKUP($A2,$F$2,7,FALSE). However, I want my Vlookup to say VLOOKUP($A2,'Roster'!A2:G,7,FALSE).
Is it possible to have the formula reference what is IN cell F2 instead of referencing just "F2"?
I hope this made sense. It's the end of my workday and I'm very tired.
try something like:
=VLOOKUP(A2, INDIRECT("Roster!A2:"&
SUBSTITUTE(ADDRESS(1, MAX(IF(Roster!1:1<>"", COLUMN(Roster!1:1), )), 4), 1, )),
MAX(IF(Roster!1:1<>"", COLUMN(Roster!1:1), )), 0)

How to calculate conflicting meeting using data from Exchange server in PowerBI

I am trying to get data insights of my calendar through visualizations in PowerBI. I am able to get almost all data from my outlook calendar using in-house API in PowerBI. I intend to find how many conflicting meetings I have per week, but I couldn't find any flag column for that. I'm trying to use time slicers to generate a what-if parameter to calculate a flag, but it doesn't work. Is there any way I can track conflicting meetings?
The data I have relative to meetings is as below -
You could add a Calculated Column to the dataset, with a formula like this:
Conflicting =
VAR StartDate = 'Calendar'[Start]
VAR EndDate = 'Calendar'[End]
VAR IDCurrent= 'Calendar'[Id]
RETURN
IF (
COUNTROWS(
FILTER (
ALL('Calendar');
'Calendar'[Start] < EndDate &&
'Calendar'[End] > StartDate &&
'Calendar'[Id] <> IDCurrent
)
) > 0; TRUE(); FALSE())
This formula checkes if there are different rows within the same date range.
You can adjust the date comparions based on your needs. I've got the logic from this post and removed the equal signs, to prevent contiguous items marked as overlapping.
The Id column is the Unique Identifier (like a unique, primairy key) automaticly provided by Exchange Online. The filter on Id <> IDCurrent makes sure you're not mark the current row as overlapping, e.g. it searches for all rows exept the current one.:
Result:
Edit: The formula above results in a true/false value. You can easily remove the if statement, to count the conflicting appointements, but remember that the value will be counted twice (or more); for each conflicting appointment.

calculated fields in TestTrack, how to add the field to itself?

i need to to add a text calculated field in TestTrack, so the field always add itself and then anoter field.
so the field will always contain the previous entry and the new one.
how can i do it ?, without the testtrack claiming its a recursive formula
the only way to dit is to do a count of the event (adding data to the field)
and than run a for each of the ocurrnces with a concatenate of the data with the string itself (old value).
here is the example that works for me :
var TicketCount=Item.Events.count("update ticket");
var ticketsStr ='';
for(ticketIndex = 0;ticketIndex < TicketCount;ticketIndex++)
{
ticketsStr = ticketsStr + Item.Events.at(ticketIndex,"update ticket").fieldValue("Customer Name");
if(ticketIndex < TicketCount-1)
ticketsStr = ticketsStr + ",";
}
result = ticketsStr;
You are correct that a TestTrack calculated field cannot reference itself in the formula. Even if it could reference itself, consider the following formula for "update ticket":
Item.fieldValue("update ticket")+Item.fieldValue("Type")
In this scenario, the Type value would always be appended but there would be no check to see if the Type value is already in the list. Every time an item is edited the "update ticket" field value would be recalculated and the Type value would be appended again whether it has changed or not.
The solution proposed by Tal solves this problem by looping through the other fields and re-building the value. Additionally if a Customer Name value is modified or deleted, the field value will be correctly calculated.