Update: File
Upon request, I am including the link to the file: here. File no longer available
I have transformed my original data into a game-like context, in which the sheet keeps track of points for completed activities and upon reaching various point amounts, the user goes up in rank. An easy way to see the issue is to enter a 1 (and then 2 and 3) into F15 next to the cell with 500. When you do so, notice how the values in Q5:Q6 change from 0 to 1, but Ranking in C2 which is the SUM function which counts this range does not increase to 3 like it should. And consequently the values in L12 and L14 which are based on it also don't update. Now, if you recalculate, the rank goes up and the L cells update. NOW, cell E3 which is based on the L cells doesn't reflect the correct value, and you have to once again recalculate the sheet for it to update.
Hope all that makes sense - let me know if you need any clarification!
Original Question
Some of my formulas are not updating right away after I have entered in data. Just to get this out of the way before someone suggests it, I DO have the calculation of the workbook set to automatic, and I DON'T open any other workbooks with their calculation method set to manual. So that's not the issue.
I would give specifics on formulas, but I don't think it has anything to do with that. The formula is a simple SUM function which adds together a range. The range updates properly - as soon as I've entered data, the numbers in the range change accordingly. The SUM function should likewise automatically update, but I have to enter data in another cell or press delete in an empty cell (basically, get the sheet to recalculate once again) to get the SUM function to reflect the changes in the range.
The only thing that I can think is that it's because I've enabled iterative calculation (in File > Options > Formulas tab) in order to allow cells to retain their previous value if a certain condition is true, else update their value. My settings are 1 for Maximum Iterations and the default of 0.001 for Maximum Change. However, I've used that before without having issues with a simple SUM function. And I have other just as simple functions in my sheet that DO update properly, so I'm a little confused...
Let me know if any more info would be helpful. Thanks for any suggestions!
UPDATE:
Sorry, I didn't catch that this is for a formula, not function. My bad.
I'll leave the answer up in case someone has the same issue, but with a function. Sorry!
Try adding this inside the code:
Application.Volatile
This will force recalculation for the function each time a cell is changed on the sheet in which this function appears.
Reference: http://msdn.microsoft.com/en-us/library/aa213653(v=office.11).aspx
You have circular references: cell formula in range S4:AL103 all refer to themselves (under cetain conditions). This will cause excel to stop calculating because it can't resolve the conflict.
You wll need to redesign your formulas
Originally added to my initial post; moved it to an actual answer so that it could be accepted
So I figured out the solution. From doing a little more reading up on iteration, I discovered that in iterative mode, Excel processes cells one at a time, in a certain order (alphabetical, I think?). So if cell A is dependent on cell B, but cell B changes value later in the process, cell A will retain a value based on cell B's old value, rather than the new one, until another recalculation is prompted... OR, until another iteration starts. So, setting maximum iteration to a higher number than 1 fixes the issue. Since I had two cells not updating in the example sheet I gave you guys, it would need to be set at about 3. In my actual sheet, I had to set it at 5.
Thanks for the thoughts! Hope this helps some other confused soul!
Related
I'm building an Excel model using iterative calculations.
The problem results in the yellow cells shown below:
One cell is linked to another and displays a different value than it should.
The bottom value (29.4%) is the correct one, so of course I would like Excel to actually use that one.
When clicking F9 (third picture) one can see that the cell value should technically be correct.
Please let me know if you need any further information as I'm new to SO. Thanks in advance!
Using circular references in Excel requires planning of the layout of the model, in order to get a stable result. And the calculation order is not the same with iterative calculations. One consequence is that if the cell referring to the "iterative cell" is above, it may show the result of the previous iteration.
Here is a very simple model demonstrating that:
A14: 1
A15: =A15 + A14
B13: =A15
B17: =A15
The following shows the results of four single iterations, one at a time. You will see that B13 is displaying the results of the previous iteration; whereas B17 displays the result of the last iteration
If I recall correctly, with iterations, calculations proceed by worksheet in alphabetical order (not sure if that refers to codename or sheetname), and on a worksheet, from left to right and top to bottom.
Summary
I need an array formula that takes a row of data of certain length from Sheet1. For that row, in each column that is not blank, I need to grab the Sheet1 header value for that column and display that data in a continuous row on Sheet2 (without any spaces in between the row's cells).
Background
I have a table of data (employees and industry certifications with expiration date being the table's cell data) on sheet 1, with a row for each employee the spreadsheet is tracking. The certifications are the columns.
We are using this information to link to ID Badge Printer software (Bodno Silver), where we are limited to linking columns of data to a particular textbox.
The problem lies in the fact that not everyone has every certification. The rows are peppered with blanks separating the certifications that each employee does have. While setting up the required text boxes in the badge software template, that each link to a specific column, I quickly realized that since not everyone has every certification if we used the data how it was we would have a bunch of strange looking blanks in between the listed certifications rather than a continuous list.
What I did
My solution to this (which I'm open to a better one if anyone knows of one, other than "use better software"), was to create a new sheet and array formulas that no one would use except for me and the id printer software. This sheet would have a similar data table that took the rows of data interspersed with blank cells between expiration dates, and put the matching column headers for cells that had a date in them into a continuous row of the same maximum length (eliminating the blank cells).
Essentially, this would allow me to circumvent the restrictions of the badge software and each textbox would be MatchedCert1, MatchedCert2, MatchedCert3, etc. up to the original maximum number of certifications.
Pictures are probably better than my words at explaining what I am going for:
Sheet1 (source)
Sheet2 (result)
The array formulas
I worked on this one for a while. What I thought would be a simple INDEX, MATCH, ISBLANK formula (that I could create using the appropriate relative and absolute cell linking) and then expand to the whole sheet turned into a witch hunt and me praying for forgiveness for my sins to all that may be holy. Also a lot of googling.... I realized quickly that this one may not be so simple after all.
Finally, I arrived at the following two array formulas in order to correctly show what I was going for:
First Column of training section
{=IFERROR(INDEX(Sheet1!$E$2:$P3,1,MATCH(FALSE,ISBLANK(Sheet1!E3:Q3),0)),"")}
(easy enough, right? I thought so...)
I felt good about this until I tried to think through what would be required to get the formula to be universal so that I could use it on the entire table.
I feel dirty just putting the following in public, but here goes...
Second column through last column array formula
{=IFNA(INDEX(INDIRECT(ADDRESS(ROW($E$2),(MATCH(E3,Sheet1!$2:$2,0)+1),1,1, "Sheet1")&":"&ADDRESS(ROW(E3),COLUMN($Q3),1)),1,MATCH(FALSE, ISBLANK(INDEX(INDIRECT("Sheet1!"&ADDRESS(ROW(E3),(MATCH(E3,Sheet1!$2:$2,0)+1),1)&":"&ADDRESS(ROW(E3),COLUMN($Q3),1)),0,0)), 0)),"")}
(please don't call the police...)
[ninja edit] While this array formula works for 2nd result column through the final column, it doesn't work if there's not a blank column following the result range. The actual spreadsheet has 4 different groups of certifications that run horizontally, but I was able to just add a blank column in the corresponding data from the other sheet easily enough, so I just let it go. I'd give somebody a nickle for the answer to why that's the case here too [/edit]
Results
The first array formula, and INDEX MATCH using ISBLANK is rather straightforward.
The biggest question for me here, and the thing that drove me absolutely nuts for a couple of days, is why the second array formula requires the additional INDEX function nested inside of the ISBLANK function.
While taking the function apart and experimenting I realized that if I have any INDIRECT reference inside a ISBLANK function, which is itself inside of a MATCH function, the result of the match was ALWAYS 1:
{=MATCH(FALSE,ISBLANK(INDIRECT("$E3:$Q3")), 0)}
The above ALWAYS returns 1, whereas if I put the range in explicitly, the function would work just fine. That wasn't an option for me, since I needed to dynamically return the starting position for the match using the previous cell's address.
However, adding an INDEX function (with a column and row value of 0) to encapsulate the INDIRECT function provides the correct answer. I figured this out just by trial and error.
Questions
Can someone with more knowledge please let me know what is causing this behavior?
As a broader question, given I am limited to using formulas (no VBA), I would also like to know if I'm going about this in the wrong way or if there is a much simpler way of accomplishing this without this behemoth of a formula?
I know this sheet will probably require maintenance in a year - good luck future self!
Put this in E3, Copy over and down
=IFERROR(INDEX(Sheet1!$2:$2,AGGREGATE(15,6,COLUMN(INDEX($E:$P,MATCH($C3,Sheet1!$C:$C,0),0))/(INDEX(Sheet1!$E:$P,MATCH($C3,Sheet1!$C:$C,0),0)<>""),COLUMN(A:A))),"")
As to why your formula is not working, it is too convoluted to parse. One note, unless the sheets is the variable, one should avoid INDIRECT as much as possible. INDEX can almost always be used in its place.
Both INDIRECT and ADDRESS are volatile functions. Volatile functions will re-calculate every time Excel re-calculates, leading to a lot of unnecessary computations.
Not a solution but to answer why you are seeing this behavior:
EDIT: PREVIOUS EXPLANATION WAS JUST PLAIN WRONG
This confused me so, I did a bit of investigation:
I think that your problem is actually coming from the ISBLANK function because it is intended to be used with single values, and cannot handle ranges. Any BLANKs which are returned by functions are only converted to numeric values (0), when the BLANK is returned to (or displayed on) the sheet. If the function is returning to another function, the BLANK value seems to be preserved.
EDIT: ADDING A SOLUTION WITHOUT ARRAY FORMULAS
This is probably more complex than using an array formula... but I strongly dislike them, so do all I can to remove them.
Firstly, I would add an index to your positions in the results sheet:
=IF(F$7>COUNTIFS($F3:$L3,"<>"),
"",
IF(
MINIFS(
$F$7:$L$7,$F$7:$L$7,
">" & IFNA(INDEX($F$7:$L$7,MATCH(E9,$F$2:$L$2,0)),0),
$F3:$L3,
"<>"
)=0,
"",
INDEX(
$F$2:$L$2,
MATCH(
MINIFS(
$F$7:$L$7,$F$7:$L$7,
">" & IFNA(INDEX($F$7:$L$7,MATCH(E9,$F$2:$L$2,0)),0),
$F3:$L3,
"<>"
),
$F$7:$L$7,
0
)
)
)
)
Basically, the formula looks at the cert in the previous cell, and looks for the next, minimum index, greater than that.
I've been struggling with this longer than I care to admit, but I have a fairly simple OFFSET function call which works on one sheet, but if I copy it to a different sheet it gives a #VALUE error.
On a sheet named "Deliverable" I have this formula in a cell:
=OFFSET(Deliverable!$B$72,1,0,,3)
and it works fine.
If I go to any other sheet and use the same exact formula, or use it in the Name Manager, it gives a #VALUE error.
If I leave off the final parameter indicated the number of columns I want, it does work:
=OFFSET(Deliverable!$B$72,1,0)
but of course isn't giving me the range I need.
Any idea what's going on with this?
I'm using Excel 2016 on Windows 7.
-- Updated Info --
In a nutshell, my spreadsheet has two cells which I'm using as dropdown lists, where the 2nd cell's list feeds off the selection in the first. The data they are based on has this format:
OptionA A B C D
OptionB A B
OptionC D E F
So the first dropdown uses a simple Data Validation source pointing to the column with OptionA, OptionB, etc. Once that's chosen, the second dropdown list should contain the appropriate options for the one selected. So if OptionB is selected, then the 2nd dropdown list should show A and B.
When I initially wrote this, the data validation source was just a simple VLOOKUP entry, but the lists often had blanks since the number of options varies for each entry. Wanting to fix it up a bit, I ended up with this formula:
=OFFSET(Deliverable!B72,Deliverable!B87,0,1,COUNTA(OFFSET(Deliverable!B72,Deliverable!B87,0,1,5)))
There won't be any more than 5 options, and there are no empty cells in the middle of the data to filter out.
In one spreadsheet I have I used this as a named range definition, then specified the named range for the cells data validation source and it worked. In this other spreadsheet however, it gave me the error described earlier.
However, it looks like when I enter the statement directly into the data validation source field and not in the name manager, it works as expected.
Am I taking the totally wrong approach?
What is it that you want this formula to do? As written, it is returning a block of three horizontal cells. The #VALUE error is Excel's way of telling you "Hey, you're trying to return three cells, but I can't fit them all in the one cell that you are calling this formula from".
The reason you see a result in some places and not others is because of something called Implicit Intersection. Give it a spin on Google. But basically, it just returns whichever one of those three results corresponds to the column that the formula is entered into. If you copy that exact same formula to say row F you will see that it returns a #VALUE error there, because it doesn't know what cell it should return given the column you're calling it from doesn't match any of the cells it is returning. The fact that you don't know this indicates that the formula you're using doesn't in fact do what you think it does.
--UPDATE --
Okay, following your further clarificaiton it seems that you're talking about Cascading Dropdowns aka Dynamic Dropdowns. Lots of info on Google about how to set these up, but you may be interested in an approach I blogged about sometime back that not only provides this functionality, but also ensures that someone can't later on go and change the 'upstream' dropdown without first clearing the 'downstream' one should they want to make a change.
Note that those links talk about a slightly complicated method compared to others, but the method has it's advantages in that it also handles more levels than two, and your DV lists are easily maintained as they live in an Excel Table.
This sounds like an array equation. Try hitting Ctrl+Shift+Enter in the other sheets to validate it as an array equation.
Whenever you need to reference ranges instead of single cells, Excel needs to know that you are working with arrays.
Without VBA, I am trying to refer a range that starts at A2 and never ends. For example, if I want row 2 till row 5 i'd do
$A$2:$A$5
But what if I want the end to be open?
$A$2:??
Is this possible?
Depending on what's in A1 and what formula you're putting the reference into, you could simply use A:A. For example, if you wanted to sum all of the values in column A, but A1 contained a column title rather than a number, you could still write =SUM(A:A) and the title in A1 would just be ignored.
A2:A works in many formulas
hope that helps
If you want to refer to a range starting from A2 until max row (1048576 or 65536 for Excel prior to 2007), you can use this volatile formula... =OFFSET(A2,0,0,(COUNTBLANK(A:A)+COUNTA(A:A)-1),1) . Use formula as a defined range name or inside other formula which takes range as an argument (for eq SUM)...
Another option (in case your formula is in A1, so accessing A:A would create a circular reference) is:
OFFSET(A2, 0, 0, ROWS(A:A)-1)
This uses ROWS to count the total number of rows (without actually accessing the rows!), subtracts 1 (because we're starting with the second row), and uses this result as the height of a range created with OFFSET.
This is another option based on a formula, using the example locations in the OP's question:
=A2:INDEX(A:A,MAX(FILTER(ROW(A:A),IF(ISBLANK(A:A),0,1)=1)))
The components are the following:
=MAX(FILTER(ROW(A:A),IF(ISBLANK(A:A),0,1)=1))
which finds the number of the deepest row that is not blank, and
A2:INDEX(A:A,<expression 1 above>)
which relies on the expression above to make a bigger formula, which obtains a range starting from any location and ending at a location in the given column at the position obtained by this expression, 1.
This is an alternative to the others listed, and may be of interest as it differs from them in potentially substantial ways.
I can note the following characteristics:
It is not necessarily fast.
It seems to NOT be a volatile formula. This is important, as it means it won't necessarily be recalculated every time a calculation is made. However, I am not sure about the frequency of calculation, and don't fully understand its volatility status.
The uncertainty is related the use of the INDEX function (and, apparently, specifically after the : in a range). There are some resources that describe it.
INDIRECT and OFFSET functions are definitely volatile. There are a number of resources that describe performance implications of volatile functions, some of them mentioned in other SO answers. For example:
https://learn.microsoft.com/en-us/office/client-developer/excel/excel-recalculation
https://www.sumproduct.com/thought/volatile-functions-talk-dirty-to-me
http://www.decisionmodels.com/calcsecretsi.htm
https://chandoo.org/wp/handle-volatile-functions-like-they-are-dynamite/
It allows the user to not have to think about the data in certain cells (for example, A1, which may be meant to have a header, and not numbers).
It returns a range between the cell specified before the : and the last cell in the column that is non-blank. I think it should include non-numeric values in its consideration as well.
It shares some commonality in terms of the range it aims to identify with the answer by Kresimir L.: =OFFSET(A2,0,0,(COUNTBLANK(A:A)+COUNTA(A:A)-1),1).
To note: This answer applies to the version of Excel available as of the time of writing as part of Office 365 (and continually updated). However, the answer is based only on my own verification of its apparent correctness of my installation. I am not sure that all installations of Office 365 have the same software exactly; and I have the sense that some features may differ among different installations (even) of Office 365. I am not sure that this answer applies to everyone. Please test. I would appreciate feedback on your success with this approach.
This well covered in VBA as code below:
Range("A2", Range("A2").End(xlDown))
And if you want reach that in formula, it depends on the version number of your MS-Excel.
According to this reference number of all rows are in a sheet from Excel 2007 onwards are 1048576 that you can use bellow:
$A$2:$A$1048576
Because this range in formula is depended on Excels version, this may be different in future versions.
Finally, I suggest you use VBA.
I am making a payroll program in Excel and one of my concerns is that the salaries of the employees are searched using the INDEX and MATCH or VLOOKUP function. The problem is if the salaries get updated in the future (e.g. a raise or changes in rates), all the previous entries that used the old salaries will be updated to the new salaries. This is a disaster and would make my entire program useless and inefficient. Therefore I need to automatically lock previous calculated cells after a certain time.
Edit: Note we do not want to do this manually such as copy pasting values only because almost all cells are connected to each other and one mistake by the encoder or if they forget to do this before updating a value, everything will be messed up.
No! Not copying and pasting, there's a simpler way. You want to convert the Formula property of a given cell (what's shown in the formula bar in Excel) into the Value property of the cell (what's shown in the cell on the spreadsheet). For a given range A1:B6 this would done by the statement
Range("A1:B6").formula = Range("A1:B6").value
But there's a quirk in Excel that you can run faster by accessing a Value2 property, so
Range("A1:B6").formula = Range("A1:B6").value2
The rest of the code is left as an exercise for the reader :-)