Dynamic reference in excel formula - vba

I have the following array formula which works for what I want to do but I'm trying to change the formula when a user selects a value.
=INDEX($A$2:$B$70,SMALL(IF($A$2:$B$70=$A$121,ROW($A$2:$B$70)),ROW(1:1))-1,1)
It's used for a monthly report and the user will choose from a drop down the day of the month, e.g 1,2,3 - 31.
So if the user selects 1 from the drop down menu I want the formula to use the above formula.
If they select 2 for example I want the formula to move over a column so it would change to
=INDEX($A$2:$C$70,SMALL(IF($A$2:$C$70=$A$121,ROW($A$2:$C$70)),ROW(1:1))-1,1)
and so on moving over a column at a time.
It this possible at all or can it even be done without VBA?
I have an example of what I want done on the following link
https://docs.google.com/spreadsheets/d/1MDOzoQxYLgW-UOyljZsMwSu8zyAB7O2k1V-bTNP5_F0/edit?usp=sharing
All the data is on the first tab called staff. Each employee has a row and the duty assigned under the corresponding day column.
On the Roster tab it summarises each day. So what I am trying to get to happen is when you choose the day of the month (or preferably the actual date) the sheet changes to reflect the data.
At the moment the code I have working does for just Day 1 because the column references are coded into the formula. I was hoping to somehow choose 6 for example from the drop down and then the formula will map chosen day to the corresponding range in the raw data and update the formula and change the formula from Staff!$A$2:$B$68 to Staff!$A$2:$G$68.
If the formula finds no more entries if shows #NUM! but I intended to use the function ISERROR() to replace #NUM! with "".
This is what I'm trying to achieve it if makes sense?

There are a few issues here/ You are returning the value from column A so the first range can be $A$2:$A$70 and that means you don't need the 1 to specify the column_num. The IF statement was covering A2:C70 when you really only want either B2:70 or C2:C70 depending on the 1 or 2.
Assuming that A122 has either a 1 or 2 in it then,
=INDEX($A$2:$A$70, SMALL(IF(INDEX($B$2:$C$70, 0, $A$122) = $A$121, ROW($1:$69)), ROW(1:1)))
Standard non-array alternative,
=INDEX($A$2:$A$70, SMALL(INDEX(ROW($1:$69)+(INDEX($B$2:$C$70, 0, $A$122) <> $A$121)*1E+99,, ), ROW(1:1)))

Related

Find number of rows in an already filtered Column A in Excel

I have got an Excel spreadsheet. This spreadsheet has just one tab in it. The Tab name is dynamic in nature and changes every week also the number of rows.
I have column A filtered already with a VBA macro. A1 has the header.
Now, I wanna find how many rows are there in this already filtered column A.
I am looking for any VBA function.
I have tried using Subtotal function.
=Subtotal(103,A2:A1345)
But I don't know the end range. As of now the end range is A1345. It will change every time in future if the new rows are added.
I tried multiple things but those did not work. I am quite new to VBA.
If A1 will never be blank, you could use (in a column other than A)
=Subtotal(103,A:A)-1.
Or, if there will be data below your table not to be counted, then format your table as a Table and use structured references (and this formula could go into column A)
=SUBTOTAL(103,Table1[column_header])
You can put the formula in column A if you use another column's last populated cell as the demarcation point.
If column B contains numbers then,
=subtotal(103, a2:index(a:a, match(1e99, b:b)))
If column B contains text then,
=subtotal(103, a2:index(a:a, match("zzz", b:b)))

Extracting "hidden" data from expanding/collapsing pivot table - Excel

I'm not sure if this is possible but as you can see I have a pivot table with multiple dependent and expandable fields. I am trying to concatenate the data from columns A:D into one cell which works fine in row 2 but doesn't work with blank parent cells, as you can see in column F.
Any ideas for how to achieve this?
Pivot table
This answer assumes that you don't want to just Repeat All Item Labels in the PivotTable from the "Report Layout" drop-down on the Pivt Table Tools "Design" tab.
A formula to get the first non-blank value on or above the same row as the current cell from Column B can be constructed with a combination of AGGREGATE, SUMPRODUCT and OFFSET, like so:
=OFFSET($B2,SUMPRODUCT(AGGREGATE(14,6,ROW($B$1:$B$100)*--(ROW($B$1:$B$100)<=ROW())*--(LEN($B$1:$B$100)>0),1))-ROW(),0)
How does it work?
Starting with the outermost part, OFFSET($B2, VALUE, 0) - this will start in cell B2, then look up or down by VALUE rows to get the value.
Next we need to know how many rows we will need to look up-or-down. Now, if we can work out the bottom-most row with data, we can subtract the current ROW() from that, giving us OFFSET($B2, NON_BLANK-ROW(),0)
So, to finish up we need to work out which rows are not blank, AND which rows are on-or-above our current row, then take the largest of those. This is going to take an ArrayFormula, but we can use SUMPRODUCT to make that calculate properly. To find the largest number we could use MAX or LARGE - but we get less errors if we pick AGGREGATE(14,6,..,1). (The 14 means "we want the kth largest number", the 6 means "ignore error values", and the 1 is k - so "we want the largest number, ignoring errors")
But, what list of numbers are we going to look at, I don't hear you ask. Well, we want the ROW for output from our range (I'm using $B$1:$B$100, because using the whole column B would take far to long to calculate repeatedly), a comparison against the current ROW(), and check that the LENgth is > 0. Those last two are comparisons, so let's write them out first:
ROW($B$1:$B100)<=ROW()
and
LEN($B$1:$B$100)>0
We want to use -- to convert TRUE and FALSE to 1 and 0 - this means that any "bad" values become 0, and any "good" values are larger than 0:
ROW($B$1:$B$100)*--(ROW($B$1:$B$100)<=ROW())*--(LEN($B$1:$B$100)>0)
This gives us the Row number when the Row is on-or-before the current row AND Column B is not blank - if either of those are False, then we get 0 instead. Stick that in the AGGREGATE to find the largest number:
AGGREGATE(14, 6, ROW($B$1:$B$100)*--(ROW($B$1:$B$100)<=ROW())*--(LEN($B$1:$B$100)>0), 1)
Then put it in a SUMPRODUCT to force Excel to treat it as an ArrayFormula, and that's your NON_BLANK. This then gives you that first formula right at the top of the post

Dynamic calculation in Excel based on a cell value

I've got a challenge in Excel i hope you guys can solve for me.
I have a drop down list (weeks) where i select the week 2015-18 to 2016-17. (nr 1 in picture)
In the cell called LY (nr 2 in picture) I want that to type the result of a dynamic sum range, based on the weeks input.
Picture of setup of the text above
The calculation logic is:
If i select week 2015-20, the VBA or formula should sum(F5:F7)/(G5:G7).
So in other words, I want a dynamic calculation that starts from week 2015-18 (F5/G5) and then sums the values down to the value that i have selected in "Weeks"
Picture of the setup of the logic values
The value of this calculation should be shown in LY (first picture, nr 2).
I really hope you can solve this for me. I've got more rows to calculate, so if you could come up with a "global" code that works for that, it would be great
ok, so you do not need VBA for this.....
for the sake of easier updates; mark the weekrange, right click them and give them a name, with Define name
I called it yearlyweeks
The two drop downs lists, will contain the value corresponding to the text indicating that week.
So we can use this to get a cells address by value (if that text is unique in the range named yearlyweeks):
=ADDRESS(MATCH(H8,yearlyweeks, 0), 2)
where H8 is the cell address of a dropdownlist cell. 2 is the column index of the "B" column
this will result in something like:
$B$2
where the 2 indicates the relative row index, in the range, not the absolute row number.
we are really not interested in the column, or relative row, only the absolute row, but so far we can live with relative..:
=ROW(INDIRECT(ADDRESS(MATCH(H8,yearlyweeks, 0), 2)))
this will give you the relative row of the cell coresponding to what you selected on the drop downlist for one of the lists. So I would do this in two calculation cells, just to avoid the next piece getting too long..
lets let the calculation cells be in I10, and J10..
I10 hold the relative row for the beginning week
J10 for the ending week
below create a new set of cells with:
="F"&(I10 + 7)
and
="G"&(J10 + 7)
where 7 is the row offset of yearlyweeks
in yet one more cell J12 we make the range string:
=CONCATENATE(I11;":";J11)
and then does the calculation in the final cell:
=SUM(INDIRECT(J12))
Now I can do this as a one liner, but you really would hate that

Sumproduct dates

I have a spreadsheet worksheet called “ISO Procedure Master List” where the date a procedure is requested is in column G (Format m/dd/yyyy).
In column H the date the procedure is completed is entered. Column H uses the same date format but may contain blanks (procedure not completed yet).
There are currently 67 rows of information in the spreadsheet that span from 2011 to 2015 and that number will continue to grow.
QUESTION, I would like to get the average time (in days) it took from a procedure request (column G) to the completion of procedure (column H) for all of the procedures requested in a given year.
In other words the average (in days) time it took to complete procedures in a given year.
This answer will be in a cell C34 in the same workbook but a different worksheet called “ISO Matrix”. (This information probably does not matter but I thought I would add I just in case)
I have tried several Sumproduct variations with no success.
Assumptions:
Dates in columns G and H are dates, not text string,
Data start in row 2, using row 1 for headings.
For convenience, I assume you have named the ranges you wish to average requestDate and completeDate
You wish do ommit the rows where the procedure is not finished from the average calculation.
After naming your ranges, input the following formula into C34 on "ISO Matrix" tab:
=AVERAGE(IF(completeDate<>"";completeDate-requestDate))
This is an array formula, so when normally pressing enter, remember to press CTRL+SHIFT+ENTER instead. When highlighting the cell, it will then look like this:
{=AVERAGE(IF(completeDate<>"";completeDate-requestDate))}
NOTE: My language pack uses ; as formula separator, so if you language uses , instead, change the formula to:
=AVERAGE(IF(completeDate<>"",completeDate-requestDate))
EDIT: So, I didn't catch that you needed to average by year. In line with my recent formula, it could be update to suit your request.
=AVERAGE(IF(completeDate<>"";IF(YEAR(requestDate)=A15;completeDate-requestDate)))
where A15 holds the year you are interested in. I tried it with an AND in the first IF instead of two IFs, but that did not seem to work. Mind you, you might still need to change ; to , and you also need CTRL+SHIFT+ENTER for this one.
You could also add the code below, to handle the case where you test a year that is not present in the list:
=IFERROR(AVERAGE(IF(completeDate<>"";IF(YEAR(requestDate)=A15;completeDate-requestDate)));"No such year")
...or something like that

Excel Formula to calculate number of user hits in a system everyday

This is the continuation of my previous questions and I have included my global aim with project.
Excel Link:
https://drive.google.com/file/d/0B5W4CgZKsARFSmhqV0ZSM1lBSkE/view?usp=sharing
Task:
The task is to collect the "number hits" of a user in an application "on each day". I can collect a log from the system which will give me the name of the users and their last hit date and total number of hits.(This total number of hits in not on daily basis, but sum of all days). and I need to make a formula to segregate these on daily basis
Screenshot:
To understand in better way I have created few screenshot, which I am looking to implement.
The excel has two tabs raw_data and value_capture. I will use raw_data to paste my raw data collecting from system log and value_capture to generate day by day hit statistics
Raw_data tab:
It has three details, 1. date. 2. name and 3 totals hits(This total hits are overall hits of that user on all days)
value_capture:
This has all user names and all dates to mark each day and a column to display all hits.
Now, considering an example of date 20141120, all users hits are 0.
raw_data shows 0 against all users
so value_capture are has to be updated with same
Now on next day 20141121, there is some hits against some users. (Note : if there is no hit against a user, it will only the previous date ie,date will update only if there is a hit on that day)
value_capture updated with that details
On 20141122, there are some more hits against the users
value_capture updated with the details. since the raw_data will show total number of hits against a user, the way of calculating on a particular will be total hits- previous date hit
I am stuck at a way to develop the excel formula here.Looking for an idea to implement this.
Note :
Users name will be fixed on both tabs, but that may come in different order in raw_data tab.
UPDATE
My raw_data sheet will be updated everyday and I want my formula results be fixed in the value_capture sheet after the values are updated (removed) from sheet raw_data
So as i mentioned in my answer to your previous question, the only way to save the results of a formula after deleting/removing its reference cells, is to use a macro.
First of all
you need a proper formula to pull the data from the raw_data sheet and then calculate the difference between that particular cell where the formula resides and the sum of previous cells in that row.
we need two formulas:one for the column B of your value_capture sheet. and the second one for the rest of your table. why? because in the first column you don't calculate the difference between itself and the previous cells, because there aren't any. So there is a little difference in the formula.
FORMULA ONE:(paste in B3 and drag and fill down)
=SUMIFS(raw_data!$C:$C;raw_data!$B:$B;$A3;raw_data!$A:$A;B$2)
FORMULA TWO:(paste in C3 and drag and fill down and to the right)
=IF(SUMIFS(raw_data!$C:$C;raw_data!$B:$B;$A3;raw_data!$A:$A;C$2)-IF(ISNUMBER(B3);SUM($B3:B3);0)>0;SUMIFS(raw_data!$C:$C;raw_data!$B:$B;$A3;raw_data!$A:$A;C$2)-IF(ISNUMBER(B3);SUM($B3:B3);0);0)
As you see in my screenshot for user in cell A3, we have value of 4 for the date 20141120 and the next day the value is 14 but the cell gets the difference, that is 10, and then then next day, the value increases to 16, so the cell value for D3 becomes 2 which is the different of 16 for that day and the sum of previous ones. This continues until today's value which is 24 so the cell gets 7 because the sum of previous ones is 17.
Now that you have your values captured correctly, with respect to the difference with the sum of previous days, it is time to make them fixed and eternal.
For this you need a macro:
Sub saveMyData()
Dim ws As Worksheet
Set ws = ActiveSheet
ws.Range("B3:L18").Copy
ws.Range("B3").PasteSpecial Paste:=xlPasteValues
Application.CutCopyMode = False
End Sub
So obviously you need to update this range "B3:L18" this is what I have in the example sheet only.
Now this macro, copies and pastes all the values from that range onto itself, so you lose your formulas.So each day, you need to go to *line 21** of the same sheet and get the formula from C21 and paste it on somewhere on row 3 from where you want to continue capturing the new day's data. (you won't need the first formula, because it is only used once in the first column, the first day.)
This was the only way to capture the values using formulas and then save the results of the formulas in their place and go and fetch new data. I couldn't think of any other strategy to make it more automatic, except for writing mova VBA and letting excel go and find where you left the day before and paste the formulas in their right place and repeat the task.
all these formulas are array formulas, so press ctrl+shift+enter to calculate the formulas.
Remember to adjust the formulas to your regional settings, i suppose, by replacing the ";" with ",".
here is the example sheet downloadable from this link
Tell me if you have problems anywhere and don't forget to answer my question in the comments about the way you update your dates in row 2 of value_capture.
The formula that you are looking for is SUMIFS
=SUMIFS([SUM RANGE],[RANGE 1],[VALUE 1],[RANGE 2],[VALUE 2])
SUM RANGE is the column of values that you want to SUM so that is your column C. For Range 1 you want to select the range of IDs. The VALUE 1 will be equal to the value in Column A Row X. For RANGE 2 it will be the Date column. For VALUE 2 it will be the value in the column header.
Put this together and you have something akin to:
=SUMIFS(raw_data!$C:$C,raw_data!$B:$B,$A3,raw_data!$A:$A,B$2)
This would be in cell B3 and you could fill the other cells from there.
If total hits for a day is a rolling count of ALL hits ever then at the end of your equation you need to subtract the sum of the prior days.
For example:
=SUM(B3:B4)
This would only need to be added to subsequent columns (not the first).
So the equation in C3 would be
=SUMIFS(raw_data!$C:$C,raw_data!$B:$B,$A3,raw_data!$A:$A,C$2)-SUM(B$3:C3)
Then in D3 it becomes
=SUMIFS(raw_data!$C:$C,raw_data!$B:$B,$A3,raw_data!$A:$A,D$2)-SUM(B$3:D3)
Try Sumifs formula
=SUMIFS(raw_data!$C:$C,raw_data!$B:$B,$A3,raw_data!$A:$A,B$2)-IF(ISNUMBER(A3),A3,0)