Creating a UDF for Count Ifs Visible and Multiple Criterias - excel

Currently I am working on a data set that has multiple filter bars that can be selected and help search the data and provide how many meet its requirements among other things. E.g. by choosing a manager and partner name it looks through those lines in the data table.
I have made it so it posts out a string of the text formula into boxes which are then concatenated and using a UDF Eval, enforced (it's needed to be broken down a lot due to lots of If and Else statements.
The thing is, as jobs are completed they are not deleted instead they are hidden.
How do I allow this data table to do a Count Ifs search with only looking at visible? Presumably its a UDF?
The ranges are from 6:1000
Below is the breakdown of the code I have. I need find a function to make this code work only on visible.
I have made it so it posts out a string of the text formula into boxes which are then concatenated and using a UDF Eval, enforced (it's needed to be broken down a lot due to lots of If and Else statements.
The thing is, as jobs are completed they are not deleted instead they are hidden.
How do I allow this data table to do a Count Ifs search with only looking at visible? Presumably its a UDF?
The ranges are from 6:1000
Below is the breakdown of the code I have. I need find a function to make this code work only on visible.
Sub SetCriteria()
If Sheet8.Range("E6").Value = 1 Then
'The 1 is displayed if there is a value placed in the filter part of the dashboard. If not this value remains 0.
Sheet8.Range("F6") = "Tank!G6:G1000,Dashboard!C6,"
'If there is a value in the filter, then the writing for the CountIf formaul is displayed, linking to the criteria.
Else: Sheet8.Range("F6") = "Tank!G6:G1000,""*"","
'If there isn't anything placed in the critieria then a wild card is selected to ensure all option for that catergory are chosen.
End If
If Sheet8.Range("E7").Value = 1 Then
'Same as above, though this time for Task Manager.
Sheet8.Range("F7") = "Tank!I6:I1000,Dashboard!C7,"
Else: Sheet8.Range("F7") = "Tank!I6:I1000,""*"","
'The two different printed formulas, depending on criteria inclusion
End If
If Sheet8.Range("E8").Value = 1 Then
'Procedure for TAS Consultant
Sheet8.Range("F8") = "Tank!J6:J1000,Dashboard!C8,"
Else: Sheet8.Range("F8") = "Tank!J6:J1000,""*"","
End If
If Sheet8.Range("E9").Value = 1 Then
'Procedure for Pillar
Sheet8.Range("F9") = "Tank!H6:H1000,Dashboard!C9)"
Else: Sheet8.Range("F9") = "Tank!H6:H1000,""*"")"
End If
End Sub
EDIT: Here is my Evaluate Function
Function Eval(Ref As String)
Application.Volatile
Eval = Evaluate(Ref)
End Function

Below array-formula to put in a cell, (Codename Sheet8 is Sheetname Sheet8 I assume)
close it with Ctrl+Shift+Enter
=SUM(SUBTOTAL(3,OFFSET(Tank!G6:G1000,ROW(Tank!G6:G1000)-MIN(ROW(Tank!G6:G1000)),,1))*(IF(Sheet8!E6=1,Tank!G6:G1000=Dashboard!C6,1))*(IF(Sheet8!E9=1,Tank!H6:H1000=Dashboard!C9,1))*(IF(Sheet8!E7=1,Tank!I6:I1000=Dashboard!C7,1))*(IF(Sheet8!E8=1,Tank!J6:J1000=Dashboard!C8)))
This formula is instead of your Sub setcriteria and your Eval function,
You could also break it down as you did before and put it in your eval function.
(Not visible rows must be filtered)
But when you go the VBA-route, have a look at looping your range, only visible rows, check for the criteria and count if met.

Related

How can Excel VBA range variables be tested for references to entire columns..?

What ways are there to test an Excel VBA range variable for references to entire columns?
I'm using Excel 2007 VBA, iterating through Range variables with For-Each loops. The ranges are passed into the function as parameters. References to individual cells, ranges of cells, and entire rows are fine.
For instance, these are okiedokie:
Range("A1") 'One cell
Range("A1:D4") 'Range of cells.
Range("10:20") 'Entire rows 10 through 20.
But if any of the ranges have references to entire columns, it will drag the function down to a screeching halt. For instance, these are not okiedokie, and they need to be tested for and avoided:
Range("A:A")
Range("A:Z")
Range("AA:ZZ")
There are a few ways I've throught of to do this, each of them plausible but with weaknesses. The code contains loops which are used for searching through cells in worksheets with many thousands of rows, so speed is critical.
Here are three ways I can think of, but I'd like to know if there are others..?
The simplest & fastest method is to count the rows. If Range(x).Rows.Count=1048576, that's the maximum number of rows in a worksheet. However, this wouldn't work if the actual number of rows turned out to be exactly that number, or if by some wild chance there were multiple overlapping areas/ranges
that all added up to that number. Both unlikely, but possible. Also, if the version of Excel changes, so might that number, thus rendering the code broken.
Use a RegEx match against the text of Range.Address(False,False) with a pattern such as ([A-Z]{1,3}):([A-Z]{1,3}). I think this would be a medium on the speed scale.
Use VBA loops, If-Then, and string functions such as InStr() and Mid() to pick at the text of Range.Address(False,False). I think this would be the slowest possible way to do it.
You could test if the range is a reference to a column by checking the Range.Address against the Range.EntireColumn.Address like this:
If Range("AA:ZZ").Address = Range("AA:ZZ").EntireColumn.Address Then
'This returns True
End If
If Range("AA1:ZZ4").Address = Range("AA1:ZZ4").EntireColumn.Address Then
'This returns False
End If
Not sure I understand the question completely but this might work for you:
Public Sub Test()
Debug.Print RowCheck(ThisWorkbook.Worksheets("Sheet1").Range("A1:A10"))
End Sub
Public Function RowCheck(InputRange As Range)
Dim u As Long 'used number of rows
Dim x As Long 'max number of rows for any column
Dim r As Long 'number of rows based on input range
With InputRange
u = Cells(Rows.Count, .Columns(1).Column).End(xlUp).Row
r = .Rows.Count
x = Rows.Count
End With
If r = x And u < r Then
RowCheck = "A bad column reference provided"
Else
RowCheck = "This is a valid reference"
End If
End Function
Ok, after reading everyone's suggestions, I realized that no matter what I do, any Range objects passed to my function might include either an entire column reference or any combination of overlapping Range references that result in an entire column being selected.
But in translation, that means...all rows in the data, aka the UsedRange. It's possible with a large amount of data the UsedRange may actually hit the last row at 1048576. And any combination of Range references passed to my Function might result in a huge area that does cover an entire column, all the way to the maximum row.
Of course the likelihood of that happening is very low, but I do like to cover all bases in my code. But the key to this puzzle is UsedRange. This creates a "synthetic maximum last row". If the GrandRange, for lack of a better name, covers all rows in the UsedRange, then my function has nothing to do and no data to return. And so a simple IF-Then-Exit should give me the solution I was looking for:
If Intersect(UsedRange,LeGrandeRange).Rows.Count = UsedRange.Rows.Count Then
'All rows in `UsedRange` are affected.
'Nothing to do.
Exit Function
Else
'Do everything here.
'Then exit normally.
...
...
...
Endif

SumIf with Strings?

This may be a stupid question, and if it is, I apologise. I have a table in excel:
Column a...........Column b
1 property1.......problem x
2 property2.......problemy
3 property3.......problemz
4 property1......problem a
I was wondering if I could use sumif (or any similar formula) to add the problems, referring to a certain property, in one cell. for ex:
I would have
Column a...........Column b
1 property1.......problem x problem a
The problem is I can't figure out where to start. I tried using sumif but I get an error. Probably because I'm trying to add strings. I tried to mix a vlookup with sumif but that didn't produce anything too. Im stuck here. Thanks for any help!
I am not 100 % sure, but I think you might need to use VBA for this. You could try to create the following custom function:
Create named ranges properties and problems in your sheet.
Click ALT+F11 to open VBA editor.
Press Insert --> Module
Write code
'
Function ConcatIF(Property As String, PropertyRange As Range, ProblemRange As Range) As String
Dim counter As Integer
Dim result As String
result = ""
Dim row As Range
counter = 1
For Each row In PropertyRange.Rows
If (Property = row.Cells(1,1).Value) Then
result = result + ProblemRange.Cells(counter,1).Value + " "
End If
counter = counter +1
Next row
ConcatIF = result
End Function
As I do not have excel on the machine I am writing this on, I could only test it on another machine, and therefore there could be spelling mistakes in this code.
Ensure that you write the code in the module you created, it cannot be written in a Sheet's code, must be module.
This function can then be called as a regular function, like sum, average and if. Create a unique list of all your properties on another sheet. Properties in column A, and then in column B you can call the cuntion. Assume row 1 is used for headings, write the following and copy down.
=ConcatIF(A2,properties,problems)
NOTE!!!! This code gets out of hand very quickly. It needs to do (number of properties) x (number of property/problem pairs) comparisons, so if this number is huge, it could slow down your sheet.
There could be faster methods, but this was from the top of my head.

Reference combined cell above

I have a sheet with a lots of columns ordered in a hierarchical way with the cells merged:
I'd like to name those columns (in example: row 5) like this:MainGroupA-SubGroupA-SubSubGroupA.
Simply referencing the columns above in the classic way won't work as the field above isn't available anymore. (In the example: the fields B1 to F1) (i.e. I can't enter A1&A2&A3 / R[-4]C&R[-3]C&R[-2]C as this formula tries to read from the "hidden" cells).
Is there a way to do this without manual work or the need to un-merge the parent-cells? I might be able to do this with some external text editor or even VBA but would prefer an "Excel formula solution" as it would stay updated for new groups and columns.
To Clarify: I'd like all columns in Line 5 to have the text like in A5
If you want:
MainGroupA-SubGroupA-SubSubGroupA
in A5 then this should work:
=A1&"-"&A2&"-"&A3
Edit Then try:
=OFFSET(A1,0,1-MOD(COLUMN(),6))&"-"&OFFSET(A2,0,MOD(COLUMN(),2)-1)&"-"&A3
though this won't give the same text as in A5 across the complete row.
The answer from pnuts is great and helped me solve some test cases. It was however a little difficult to adapt and produced empty strings for the last column, so I also wrote a VBA-Function to do exactly what I need.
Open the VBA Editor (ALT + F11) and enter the following code in a new module:
Public Function checkLeftIfEmpty(start As range) As String
If start.Cells.Count > 1 Then
checkLeftIfEmpty = "Only a single cell allowed as parameter"
Exit Function
End If
Dim currentRange As range
Set currentRange = start
Do While currentRange.Column >= 1
If currentRange.Value <> "" Then
checkLeftIfEmpty = currentRange.Value
Exit Function
Else
Set currentRange = currentRange.Offset(0, -1)
End If
Loop
End Function
You can now use the function checkLeftIfEmpty to find the first cell left-side from your parameter which contains text: (This will be the text of the merged cell itself, if applied to a "hidden by merge" cell)
And also in combination to concatenate a string:

Excel spread sheet if statement

I'm working on an excel spreadsheet and want an if statement in a cell that allows user input if a certain condition is met, and calculates a value otherwise. Something like
=if(condition true, whatever user wants, 5*$A$1,)
Is there a way to do this?
You won't be able to have the user-input in the same cell as your formula. (without using VBA)
To do it without VBA you will need to use at least 2 cells, one with your formula, and one for the user value
There are a couple of ways you can do it with VBA
Heres a simple one, but would not really recomment it, if lots of cells use this it you'll get lots of inputboxes!
usage: =IF(condition, UserInput(), false result)
Public Function UserInput() As Integer ' used integer as an example
Dim Result As Variant
Result = Application.InputBox("Enter an Integer", "Input Required", , , , , , 1) ' inputbox, the final 1 makes it only accept numbers
If VarType(Result) = vbBoolean Then
UserInput = 0 ' the default value
Else
UserInput = CInt(Result) ' make sure its an integer
End If
End Function
Another one, would involve using the selection change and cell change events to read the initial value of the cell being changed, and allow the change (adding the value into the initial formula's "true" block or deny the changes by reverting the cells formula to the initial one.
You either need to use a Macro to update only null columns or you need to allow user to enter values in another column and then merge the values in this column, third option is to fill it with formulas and allow people to edit it to any value if they want only values
=IF(C11="Economic",120,IF(C11="DBServer",480,IF(C11="Gamer",120,IF(C11="Custom",M15,"null"))))
My example was to build an optimal computer given certain constraints. There was a drop down with Economic, DBServer, Gamer, and Custom as options. If you chose economic, then 120 would show up in the cell, DbServer meant 480, etc. If you selected custom, then it would refer to cell M15 which was a user input that didn't affect the code of the cell you wanted the final number in.

Writing an input integer into a cell

I am writing a quick application myself - first project, however I am trying to find the VBA code for writing the result of an input string to a named cell in Excel.
For example, a input box asks the question "Which job number would you like to add to the list?"... the user would then enter a reference number such as "FX1234356". The macro then needs to write that information into a cell, which I can then use to finish the macro (basically a search in some data).
You can use the Range object in VBA to set the value of a named cell, just like any other cell.
Range("C1").Value = Inputbox("Which job number would you like to add to the list?)
Where "C1" is the name of the cell you want to update.
My Excel VBA is a little bit old and crusty, so there may be a better way to do this in newer versions of Excel.
I recommend always using a named range (as you have suggested you are doing) because if any columns or rows are added or deleted, the name reference will update, whereas if you hard code the cell reference (eg "H1" as suggested in one of the responses) in VBA, then it will not update and will point to the wrong cell.
So
Range("RefNo") = InputBox("....")
is safer than
Range("H1") = InputBox("....")
You can set the value of several cells, too.
Range("Results").Resize(10,3) = arrResults()
where arrResults is an array of at least 10 rows & 3 columns (and can be any type). If you use this, put this
Option Base 1
at the top of the VBA module, otherwise VBA will assume the array starts at 0 and put a blank first row and column in the sheet. This line makes all arrays start at 1 as a default (which may be abnormal in most languages but works well with spreadsheets).
When asking a user for a response to put into a cell using the InputBox method, there are usually three things that can happen¹.
The user types something in and clicks OK. This is what you expect to happen and you will receive input back that can be returned directly to a cell or a declared variable.
The user clicks Cancel, presses Esc or clicks × (Close). The return value is a boolean False. This should be accounted for.
The user does not type anything in but clicks OK regardless. The return value is a zero-length string.
If you are putting the return value into a cell, your own logic stream will dictate what you want to do about the latter two scenarios. You may want to clear the cell or you may want to leave the cell contents alone. Here is how to handle the various outcomes with a variant type variable and a Select Case statement.
Dim returnVal As Variant
returnVal = InputBox(Prompt:="Type a value:", Title:="Test Data")
'if the user clicked Cancel, Close or Esc the False
'is translated to the variant as a vbNullString
Select Case True
Case Len(returnVal) = 0
'no value but user clicked OK - clear the target cell
Range("A2").ClearContents
Case Else
'returned a value with OK, save it
Range("A2") = returnVal
End Select
¹ There is a fourth scenario when a specific type of InputBox method is used. An InputBox can return a formula, cell range error or array. Those are special cases and requires using very specific syntax options. See the supplied link for more.
I've done this kind of thing with a form that contains a TextBox.
So if you wanted to put this in say cell H1, then use:
ActiveSheet.Range("H1").Value = txtBoxName.Text

Resources