Google Sheets - formula for expense sheet - if-statement

I need a formula that will help me calculate the $ expense amount for my milage rates.
Here is my sheet https://docs.google.com/spreadsheets/d/1PPFuFWbxWi9iIdtYBJvnSNB1j1cjjQutepF3hnpGoFM/edit?usp=sharing
On the tab that's expenses, I need to check the year of my date in Col A (because different years have different rates), then check Col D to see if it says Milage, then check Col E for what type of milage (there are 4 types), then return the number input in Col I times the appropriate rate for that year and type of milage. I have a table set up with the rates in another tab.
I'm thinking it will be a long IF and AND formula. Any help would be great!

In 'Essentials Log', I've changed the format of your year start-end dates to state the year you're referencing. I've added a column for "Milage".
Expense Reference Screenshot
In 'Expenses'!M2, I tried this and it seems to work:
=ARRAYFORMULA(iferror(if(isblank(A2:A),"",I2:I*vlookup(D2:D&year(A2:A)&E2:E,query({'Essentials Log'!Y2:Y&'Essentials Log'!Z2:Z&'Essentials Log'!AA2:AA,'Essentials Log'!AB2:AB},"Select *"),2,false))))
Working Example Screenshot
Have a look (I copied your example sheet): Milage expense

I solved this with creating an app script.
I realized I didn't want to use a formula because in my $ col most entries will be keyed in and so I didn't want to copy and paste a formula every time.
Here is the code if anyone might need it. I added it to the menu so the calculation is done with a click.
function expenseMilageCalculation() {
var auditionsSheet = SpreadsheetApp.getActiveSpreadsheet();
var expensesTab = auditionsSheet.getActiveSheet();
var activeCell = expensesTab.getActiveCell();
var activeRow = activeCell.getRow();
var expenseDate = expensesTab.getRange(activeRow, 1).getValue();
var expenseYear = expenseDate.getFullYear();
var expenseType = expensesTab.getRange(activeRow, 4).getValue();
var expenseDetails = expensesTab.getRange(activeRow, 5).getValue();
var numMiles = expensesTab.getRange(activeRow, 9).getValue();
var essentialsLogTab = auditionsSheet.getSheetByName("Essentials Log")
//2018 Business Milage
if (expenseYear == 2018 && expenseType == "Milage" && expenseDetails == "Business") {
var milageBusinessRate2018 = essentialsLogTab.getRange(3, 27).getValue();
var dollarMilesDeduction = numMiles * milageBusinessRate2018
expensesTab.getRange(activeRow, 8).setValue(dollarMilesDeduction)
}
//2019 Business Milage
if (expenseYear == 2019 && expenseType == "Milage" && expenseDetails == "Business") {
var milageBusinessRate2019 = essentialsLogTab.getRange(8, 27).getValue();
var dollarMilesDeduction = numMiles * milageBusinessRate2019
expensesTab.getRange(activeRow, 8).setValue(dollarMilesDeduction)
}

Related

Google Sheets Apps Script - How to add an Arrayformula and multiple associated IF functions within a script (Without showing the formula within UI)

I was wondering if someone is able to assist?
I'm trying to add an Arrayformula consisting of two IF functions, so I'm wanting to merge the following two formulas into one cell:
=ARRAYFORMULA(IF(D13:D104="","",(IF(K13:K104,K13:K104*20,"$0"))))
=ARRAYFORMULA(IF(D105:D="","",(IF(K105:K,K105:K*C4,"$0"))))
So the first section of the sheet needs to be multiplied by 20, and then the figure has changed and needs to be multiplied by 25 (which is cell C4)
Is it possible to merge these into one cell containing an Arrayformula+the two IF functions (or is there another/easier way for this)
Is it possible to add this into Google Apps Script so that it works in the backend (so not just a script that applies the formula into a cell - but doesn't show in the frontend or sheet)
More of a general question - When using Arrayformula with IF; and for example the output is specific text e.g. "Test Complete" associated to the range F2:F (checking if E2:E contains a particular phrase e.g. "Done") - for the empty cells in between (due to setting the False outcome as "") is it possible to somehow randomly add data into these blank cells without interrupting the formula? (so I have to option for automated text if the cell to the left states a particular term/word, but still have the option to manually add random data into blank cells)
Any assistance would be greatly appreciated
As for 1st and 2nd questions: it looks like a task for a custom function. Something like this:
function MULTI() {
var sheet = SpreadsheetApp.getActiveSheet();
var cell = sheet.getActiveCell();
var row = cell.getRow();
var value = sheet.getRange('K'+row).getValue();
return (row < 105) ? value * 20 : value * 25;
}
It gets a value from column K and multiplies it by 20 if the row less than 105 and by 25 for the rest of rows.
Here is the variant of the same formula that uses the cell 'C4':
function MULTIC4() {
var sheet = SpreadsheetApp.getActiveSheet();
var cell = sheet.getActiveCell();
var row = cell.getRow();
var value = sheet.getRange('K'+row).getValue();
var c4 = sheet.getRange('C4').getValue();
return (row < 105) ? value * 20 : value * c4;
}
And it can be done with the trigger onEdit():
function onEdit(e) {
var col = e.range.columnStart;
if (col != 11) return; // 11 = K
var sheet = e.source.getActiveSheet();
if (sheet.getName() != 'Sheet1') return;
var c4 = sheet.getRange('C4').getValue();
var row = e.range.rowStart;
var dest_cell = sheet.getRange('D'+row);
var value = sheet.getRange(row,col).getValue();
var result = (row < 105) ? value * 20 : value * c4;
dest_cell.setValue(result);
}
It recalculates automatically the value in the cell of column 'D' (current row) every time when you're changing value in the cell of column 'K'. On the sheet 'Sheet1'.

Occurrences of a value in a table in Power BI

I got articles from PubMed, I am using Power BI to visualise How many articles are written by each author, each article is written by many authors as shown in the picture any idea to do that, this is my table structure in power bi. What I want to get is a chart visualising :
name of author: number of articles
One other thing is to visualise collaboration between authors, I mean how many time each pair of authors worked together,
any ideas, I searched about it but not result that I need just an orientation.
First if you want to show how many article writes a specific author then you need a list of authors in one column (if you dont have dictionary then you can create this list as NewTable in dax):
AuthorsList = SUMMARIZE(UNION( VALUES(pubmed[name1]), VALUES(pubmed[name2]), VALUES(pubmed[name3])), pubmed[name1])
Then we can create a measure:
ArticleCount =
var __author = SELECTEDVALUE(AuthorsList[name1])
return
CALCULATE( COUNTROWS(VALUES(pubmed[ID])), FILTER(ALL(pubmed), pubmed[name1] = __author || pubmed[name2] = __author || pubmed[name3] = __author))
And for collaboration, I think we can create a cross join ListOfAuthors and use measures like this one (but I don't test it).
ArticleCount =
var __author = SELECTEDVALUE(AuthorsList[name1])
var __author2 = SELECTEDVALUE(AuthorsList[name2])
var __author3 = SELECTEDVALUE(AuthorsList[name3])
return
CALCULATE( COUNTROWS(VALUES(pubmed[ID])), FILTER(ALL(pubmed),
(pubmed[name1] = __author || pubmed[name2] = __author || pubmed[name3] = __author) &&
(pubmed[name1] = __author2 || pubmed[name2] = __author2 || pubmed[name3] = __author2) &&
(pubmed[name1] = __author3 || pubmed[name2] = __author3 || pubmed[name3] = __author3)
)
)

LOOP to Look up the value in M Query

I have a following data table:
I need to create a column called FINAL VALUE with certain rules in the power query editor:
The FINAL VALUE is created based on the COUNTRY
If VALUE is greater than 100, then the previous value will be taken
if the previous values (no previous value such as 202001) are all greater than 100, then 100
So the final table should look like:
I sort COUNTRY and DATE, and then I try to apply loop in M Query. As I am not familiar with the function in M query, I have hard time figuring out. I appreciate any help. Thank you.
I believe you are better of in DAX. You can create a new column like below:
Final Value =
var curCountry = TableCountry[Country]
var curDate = TableCountry[Date]
var prevDate = CALCULATE(MAX(TableCountry[Date]), FILTER(TableCountry, curDate >= TableCountry[Date] && curCountry = TableCountry[Country] && TableCountry[Value] <=100))
return LOOKUPVALUE(TableCountry[Value], TableCountry[Country], curCountry, TableCountry[Date], prevDate, 100)

Google Sheets Search and Sum in two lists

I have a Google Sheets question I was hoping someone could help with.
I have a list of about 200 keywords which looks like the ones below:
**List 1**
Italy City trip
Italy Roundtrip
Italy Holiday
Hungary City trip
Czechia City trip
Croatia Montenegro Roundtrip
....
....
And I then have another list with jumbled keywords with around 1 million rows. The keywords in this list don't exactly match with the first list. What I need to do is search for the keywords in list 1 (above) in list 2 (below) and sum all corresponding cost values. As you can see in the list below the keywords from list 1 are in the second list but with other keywords around them. For example, I need a formula that will search for "Italy City trip" from list 1, in list 2 and sum the cost when that keyword occurs. In this case, it would be 6 total. Adding the cost of "Italy City trip April" and "Italy City trip June" together.
**List 2** Cost
Italy City trip April 1
Italy City trip June 5
Next week Italy Roundtrip 4
Italy Holiday next week 1
Hungary City holiday trip 9
....
....
I hope that makes sense.
Any help would be greatly appreciated
try:
=ARRAYFORMULA(QUERY({IFNA(REGEXEXTRACT(PROPER(C1:C),
TEXTJOIN("|", 1, SORT(PROPER(A1:A), 1, 0)))), D1:D},
"select Col1,sum(Col2)
where Col1 is not null
group by Col1
label sum(Col2)''", 0))
You want to establish whether keywords in one list (List#1) can be found in another list (List#2).
List#2 is 1,000,000 rows long, so I would recommend segmenting the list so that execution times are not exceeded. That's something you will be able to establish by trial and error.
The solution is to use the javascript method indexOf.
Paraphrasing from w3schools: indexOf() returns the position of the first occurrence of a specified value in a string. If the value is not found, it returns -1. So testing if (idx !=-1){ will only return List#1 values that were found in List#2. Note: The indexOf() method is case sensitive.
function so5864274503() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var srcname = "source";
var tgtname = "target";
var sourceSheet = ss.getSheetByName(srcname);
var targetSheet = ss.getSheetByName(tgtname);
// get the source list
var sourceLR = sourceSheet.getLastRow();
var srcData = sourceSheet.getRange(1,1,sourceLR).getValues();
//get the target list
var targetLR = targetSheet.getLastRow();
var tgtlist = targetSheet.getRange(1,1,targetLR,2).getValues();
var totalcostvalues = [];
// start looping through the keywords (list 1)
for (var s = 0;s<srcData.length;s++){
var totalcost = 0;
var value = srcData[s][0]
// start looping through the strings (List 2)
for (var i=0;i<tgtlist.length;i++){
// set cost to zero
var cumcost = 0;
// use indexOf to test if keyword is in the string
var idx = tgtlist[i][0].indexOf(value);
// value of -1 = no match, value >-1 indicates posuton in the string where the key word was found
if (idx !=-1){
var cost = tgtlist[i][1]
cumcost = cumcost + cost;
totalcost = totalcost+cost
}
}//end of loop - list2
//Logger.log("DEBUG: Summary: "+value+", totalcost = "+totalcost)
totalcostvalues.push([totalcost])
}// end of loop - list1
//Logger.log(totalcostvalues); //DEBUG
sourceSheet.getRange(1,2,sourceLR).setValues(totalcostvalues);
}
I also got this one, but it's case sensitive a bit
function myFunction() {
var ss = SpreadsheetApp.getActive();
var sheet1 = ss.getSheets()[0];
var sheet2 = ss.getSheets()[1];
var valuesSheet1 = sheet1.getRange(2,1, (sheet1.getLastRow()-1), sheet1.getLastColumn()).getValues();
var valuesCol1Sheet1 = valuesSheet1.map(function(r){return r[0]});
var valuesCol2Sheet1 = valuesSheet1.map(function(r){return r[1]});
Logger.log(valuesCol2Sheet1);
var valuesSheet2 = sheet2.getRange(2,1, (sheet2.getLastRow()-1)).getValues();
var valuesCol1Sheet2 = valuesSheet2.map(function(r){return r[0]});
for (var i = 0; i<= valuesCol1Sheet2.length-1; i++){
var price = 0;
valuesCol1Sheet1.forEach(function(elt,index){
var position = elt.toLowerCase().indexOf(valuesCol1Sheet2[i].toLowerCase());
if(position >-1){
price = price + valuesCol2Sheet1[index];
}
});
sheet2.getRange((i+2),2).setValue(price);
};
}

PowerBi subtracting two cells in different rows with condition

I am wondering if something that i would like to achieve is possible, please look at the picture and read description below:
I would like to add a column to the right, where if a cell table[ActionType] = "TERMINATING", it calculates a difference between timestamps (timestamp for TERMINATING - timestamp for STARTING in below row). If the result is positive (>0) then store it in a column in a corresponding row (eg next to timestapm for terminating), if result is negative don't store it. And all of that applied to whole table.
I tried conditional column and i guess it cannot be done with this or at least I couldn't make it.
Will be very thankful for responses and tips!
Pre-requisite :- Add an Index Column using the query editor. Make sure they are in the next row to each other.
It is advisable to keep TimeStamp column as a DateTime Column itself.
So, if you can change your TimeStamp column to a DateTime column then try this :-
Difference =
Var Get_Action_Type = Table1[ActionType]
Var required_Index_1 = Table1[Index] + 1
Var required_Index = IF(required_Index_1 > MAX(Table1[Index]),required_Index_1-1, required_Index_1)
Var Current_Action_TimeStamp = Table1[TimeStamp]
Var next_Action_TimeStamp = CALCULATE(MAX(Table1[TimeStamp]),FILTER(Table1, Table1[Index] = required_Index))
Var pre_result = IF(Get_Action_Type = "TERMINATING", DATEDIFF(Current_Action_TimeStamp, next_Action_TimeStamp,SECOND), BLANK())
Var result = IF(pre_result > 0, pre_result, BLANK())
return result
And if you cannot change it to a Date Time, then try this calculated column,
Difference_2 =
Var Get_Action_Type = Table1[ActionType]
Var required_Index_1 = Table1[Index] + 1
Var required_Index = IF(required_Index_1 > MAX(Table1[Index]),required_Index_1-1, required_Index_1)
Var Current_Action_TimeStamp = Table1[Time_Stamp_Number]
Var next_Action_TimeStamp = CALCULATE(MAX(Table1[Time_Stamp_Number]),FILTER(Table1, Table1[Index] = required_Index))
Var pre_result = IF(Get_Action_Type = "TERMINATING", next_Action_TimeStamp - Current_Action_TimeStamp, BLANK())
Var result = IF(pre_result > 0, pre_result, BLANK())
return result
The Output looks as below :-
Kindly accept the answer if it helps and do let me know, how it works for you.