Search for Row in Range Matching Multiple Criteria - excel

I am trying to search through a range for the row matching multiple criteria in Excel VBA. Basically, when I cell is double-clicked, I need to know if the clicked value exists in another sheet, but meeting two additional criteria as well.
I am using the Application.WorksheetFunction method, trying to implement this Excel formula:
=MATCH(1,INDEX((A1=rng2)*(B1=rng3)*(C1=rng4),0,1)
This formula works in the spreadsheet (directly in Excel, not VBA), returning the correct row number:
=MATCH(1,INDEX((TRIM("nasdbpha04")=$D$3:$D$2000)*("Local"=$P$3:$P$2000)*($AQ$1=$AQ$3:$AQ$2000),0,1),0)
When I try to implement the same in the WorkSheet_BeforeDoubleClick event in VBA, the following gives a Run-time error '13' - Type mismatch. My code is below:
Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As Boolean)
Dim row As Long
Dim dtMax As Date 'corresponds to $AQ$1 above (only put in $AQ$1 to test the formula)
Dim rng1 As Range
Dim rng2 As Range
Dim rng3 As Range
Dim rng4 As Range
Set rng2 = Sheets("DB2 Databases").Range("$d$3:$d$2000")
Set rng3 = Sheets("DB2 Databases").Range("$p$3:$p$2000")
Set rng4 = Sheets("DB2 Databases").Range("$aq$3:$aq$2000")
dtMax = Application.WorksheetFunction.Max(rng3)
row = Application.WorksheetFunction.Match(1, Application.WorksheetFunction.Index((Trim(Target.Value) = rng2) * ("Local" = rng3) * (dtMax = rng4), 0, 1), 0)
End Sub
Comparing variables in the Sub to the formula:
Target.Value = "nasdbpha04" and
dtMax = $AQ$1 in the spreadsheet (but only in the spreadsheet for testing purposes)
I know I could alternately write a function to perform a find command and then check to see if the other two conditions are met on that row, and if not, iterate through until none are left to be found. However, since the formula works in Excel, I figure there should be a way to have it work in VBA using WorksheetFunction.
Any suggestions on what I should correct or do differently would be appreciated.
Thank you.

Related

Can not SUM selected cells With SUMIF

I would like to be able to Check for criteria "owners" In column A and SUM cell from the same row column E with the cell above and repeat through the spread sheet where owner is found.
I have tried to use Function =SUMIF(A3,"*owner*",E2:E3) This returns the Value above the cell in the row with owner but does not SUM them.
I have also tried this VBA CODE and it does the same thing.
Sub vba_sumif()
Dim gRange As Range
Dim sRange As Range
Set gRange = Range("A3")
Set sRange = Range("E2:E3")
Range("G2") = _
WorksheetFunction.SumIf(gRange,"*Owner*", sRange)
End Sub
Ideally it would return the summed cells in the above cell. using VBA
Thanks again,
Aaron
Well first of all, I would suggest just using the formula unless you have a reason not to:
Formula:: Your first error with the formula is both ranges need to be the same size.
VBA:: Same thing here with range sizes, then you also need to remember to make sure Excel knows what sheet you're referring to. I like to do this with a With My_Worksheet_Namestatement, then include a . before any range from that sheet. Just make sure to change the sheet name to your sheet name
Option Explicit
Sub vba_sumif()
Dim TableSheet As Worksheet
Dim gRange As Range
Dim sRange As Range
Set TableSheet = Worksheets("TableSheet")
With TableSheet
Set gRange = .Range("A2:A13")
Set sRange = .Range("E2:E13")
.Range("G2").Value = _
WorksheetFunction.SumIf(gRange, "*Owner*", sRange)
End With
End Sub

Excel VBA - how to check for non-blank values in hidden range

I'll start by making my objective clear, and then explaining it fully.
My goal is to check for non-blank values in a range, but only in the hidden cells of that range, and then use conditional formatting in a different cell, depending on whether the cells in the range are empty or not.
I have a named range called Location_Address_RangeCheck that covers the cells directly to the right of the location numbers, like this (location numbers are not part of the range).
When the Number of Locations is changed, the rows that go beyond that number (up to 25) are automatically hidden on worksheet_change to reduce clutter and reduce scrolling to see the stuff below it. That code works fine, so I'm not posting it here so as to not confuse anyone with what I'm trying to accomplish.
I want to provide a safeguard to ensure that there aren't values in the hidden rows that could affect outputs (i.e., if someone selects "3" for Number of Locations, but there is data in cells that might be on the row of the 8th location).
My goal is to check for non-blank values in the range, but only in the hidden cells, and then use conditional formatting in the cell next to the number of locations chosen, depending on whether the cells in the range are empty or not.
So if there is data in the hidden cells, then it would cause the sheet to look like this
.
I've tried so many different things so far, but I'm not making any progress. I've scoured the internet trying to find a solution, but everything I've found is about finding things in visible cells, which is the opposite of what I'm trying to achieve.
Here is the code I have written so far, which I know does not achieve my objective:
Sub testhiddencells()
Dim myRange As Range
Set myRange = Range("Location_Address_RangeCheck")
NumRows = Application.WorksheetFunction.CountA(myRange)
If Range("Location_Address_RangeCheck").Hidden = True Then
If Application.WorksheetFunction.CountA(Range("Location_Address‌​_RangeCheck")) <> 0 Then
MsgBox "There's something there"
End If
End If
End Sub
Here is a minimal example to check with a cell is both hidden and is non-empty:
Option Explicit
Sub Test()
Dim ws As Worksheet
Dim rngToCheck As Range
'test range - all cells populated with 'a' and 3 are hidden
Set ws = ThisWorkbook.Worksheets("Sheet1")
Set rngToCheck = ws.Range("A1:A7")
If TestForNonBlankCellsInHiddenRange(rngToCheck) Then
'do you conditional format stuff here
End If
End Sub
Function TestForNonBlankCellsInHiddenRange(rngToCheck As Range) As Boolean
Dim rngCell As Range
Dim blnCheck As Boolean
'assume that hidden cells are blank
blnCheck = False
'iterate range
For Each rngCell In rngToCheck
If rngCell.EntireRow.Hidden And Not IsEmpty(rngCell.Value) Then
'found a hidden and non-empty cell
blnCheck = True
'debug address of this cell
Debug.Print rngCell.Address
End If
Next rngCell
'return check
TestForNonBlankCellsInHiddenRange = blnCheck
End Function
Looking at the code you used already, you should be able to adapt this to the particular use case of your worksheet.

How to use a For loop with Vlookup in Excel using Variables to identify cells

1) My objective is the following one:
To replace the content of a cell with a conditional statement that is either "Monitorato" or "Non Monitorato". The Condition determining the type of statement is the output of a Vlookup function. IF vlookup finds a value I would have "Monitorato", while if this does not happen I would have "Non Monitorato".
2) By using traditional excel functions, the thing is solved In this way:
=IF(ISNA(VLOOKUP(cell to look for, range, column, false)),"Non Monitorato","Monitorato")
I then copy and paste this formula to all the cells below the one I wrote the initial formula
3) However I would like to implement this thing using VBA.
Moreover, there is an additional problem. The data are actually filtered, so I need to use the Vlookup function only on the filtered data. The Vlookup function should look for the first visible cell in the dataset.
4) The horrible code that I wrote is the following one:
Sub MyFunction()
Dim i As Long
Dim LastRow As Long
Dim FirstRow As Long
Dim Header As Range
Set Header = Range("d1")
FirstRow = Range(Header.Offset(1, 0), Header.End(xlDown)).SpecialCells(xlCellTypeVisible).Cells(1).Count
LastRow = ActiveSheet.UsedRange.Rows.SpecialCells(xlCellTypeVisible).Count
For i = FirstRow To LastRow
Sheets(1).Cells(FirstRow, 5) = Application.WorksheetFunction.VLookup(Sheets(1).Cells(i, 3), Sheets(1).Range("C2:D100"), 2)
Next i
End Sub
The error I am getting is
Unable to get the vlookup property of the worksheet function class
I would like to solve it, but I also know there must be an easier solution to the problem...
Thank you all!
There's actually several issues with the current code and it will not accomplish what you are after. See if this code works for you:
Sub Monitorato()
Dim ws1 as Worksheet
Set ws1 = Sheets(1)
Dim rLookup As Range, rCell as Range
With ws1
Set rLookup = .Range(.Range("D1"),.Range("D1").End(xlDown)).SpecialCells(xlCellTypeVisible)
For each rCell in rLookup
If Not .Range("C2:D100").Find(rCell.Offset(,-1).Value2) Is Nothing Then
rCell.Offset(,1).Value = "Monitorato"
Else
rCell.Offset(,1).Value = "Non-Monitorato"
End If
Next
End With
End Sub`

How to ignore filtered-out data in Excel formula

I am using an Index/Match to get data from a related table to populate in the first table. In my related table I have filtered out values, but the filtered out values are still populating in my first table. If Index/Match is not smart enough to only grab the filtered values, how can I work around this (formula preferred, but VBA acceptable) to get only the filtered values.
Here is my current formula:
=INDEX(Table_owssvr__1[MyValues],MATCH([#[ID]],Table_owssvr__1[ID],0))
You might find the SUBTOTAL function useful, as it only works on visible rows. (Here's some more general discussion about SUBTOTAL)
But if that's not flexible enough for your needs, here's how to check whether a certain cell is filtered out or not.
Using this, I've written a bit of VBA code to sum over a column summing only visible cells. Should be a pretty useful start in doing whatever you need to do.
If summing over the cells is not what you want to do, just change the part indicated in the comments. (Obviously you'd have to change the name of the function from sumFilteredColumn to something else!)
Public Function sumFilteredColumn(startCell As Range)
Dim lastRow As Long ' the last row of the worksheet which startCell is on
Dim currentCell As Range
Dim runningTotal As Long ' keeps track of the sum so far
lastRow = lastRowOnSheet(startCell)
Set currentCell = startCell
' Loop until the last row of the worksheet
Do While currentCell.Row <= lastRow
' Check currentCell is not hidden
If Not cellIsOnHiddenRow(currentCell) Then
' -------------------------------------------------
' Here's where the magic happens. Change this to
' change sum to, e.g. concatenate or multiply etc.
If IsNumeric(currentCell.Value) Then
runningTotal = runningTotal + currentCell.Value
End If
' -------------------------------------------------
End If
Set currentCell = currentCell.Offset(1) ' Move current cell down
Loop
sumFilteredColumn = runningTotal
End Function
' return the number of the last row in the UsedRange
' of the sheet referenceRange appears in
Public Function lastRowOnSheet(referenceRange As Range) As Long
Dim referenceSheet As Worksheet
Dim referenceUsedRange As Range
Dim usedRangeCellCount As Long
Dim lastCell As Range
Set referenceSheet = referenceRange.Parent
Set referenceUsedRange = referenceSheet.usedRange
usedRangeCellCount = referenceUsedRange.Cells.CountLarge
Set lastCell = referenceUsedRange(usedRangeCellCount)
lastRowOnSheet = lastCell.Row
End Function
' Is the row which referenceCell is on hidden by a filter?
Public Function cellIsOnHiddenRow(referenceCell As Range) As Boolean
Dim referenceSheet As Worksheet
Dim rowNumber As Long
Set referenceSheet = referenceCell.Parent
rowNumber = referenceCell.Row
cellIsOnHiddenRow = referenceSheet.Rows(rowNumber).EntireRow.Hidden
End Function
LondonRob mentioned the SUBTOTAL function. AGGREGATE is a more general function than SUBTOTAL that operates with knowledge of both hidden and filtered cells (there is a difference). They'll do that without addins or VBA, though with somewhat hard-to-read formulae.
I learnt it from here.
I have been able to get this working by the following:
1) Create three worksheets, one for clients, one for purchases, and one for purchasesforclient.
2) Create a Macro to copy filtered values to a new worksheet:
Sub Purchases()
Dim Rng As Range
Set Rng = Worksheets("Comments").Columns("A")
Set Rng = Rng.Resize(65535, 1).Offset(1, 0)
Set Rng = Rng.Resize(, 5).SpecialCells(xlCellTypeVisible)
Rng.Copy Worksheets("PurchasesforClient").Range("A2")
End Sub
3) When I update the purchases via a filter, I run the macro in step 2 by creating a subtotal field and triggering the macro as follows. Since it is a formula, it requires a calculation to occur. This is embedded in the purchases sheet as VBA where the filtering is occurring, where B23 is the subtotal field that changes when it counts the amount of items once a filter is applied:
Public CurrentValue As Double
Private Sub Worksheet_Activate()
CurrentValue = Application.WorksheetFunction.Sum(ActiveSheet.Range("B23"))
End Sub
Private Sub Worksheet_Calculate()
If Application.WorksheetFunction.Sum(Range("B23")) <> CurrentValue Then Purchases
End Sub
4) I use the now filtered values in the purchasesforclient worksheet for my index/match formula in the clients worksheet. This allows me to dynamically filter by date, purchase type, etc. and have updated information in the clients worksheet
This answer requires MOREFUNC addon*
=INDEX(ARRAY.FILTER(Table_owssvr__1[MyValues]),MATCH([#[ID]],ARRAY.FILTER(Table_owssvr__1[ID]),0))
ARRAY.FILTER() function "Stores only the visible cells of a range (for instance a filtered range) in an array and returns this array. "
*MOREFUNC ADDON
Morefunc Addon is a free library of 66 new worksheet functions.
HERE is some information (by original author)
here is the last working download link I found
here is a good installation walk-through video

VBA UDF gives different answer when called from Excel vs VBA

The following VBA function counts the number of cells containing formulas in a given range. It works correctly when called from a VBA sub. When called from Excel, it returns the total number of cells in the range.
The call from Excel is =CountFormulas(A1:C7), which returns 21 even though only two cells with formulas are in the range.
What is causing this discrepancy?
Public Function CountFormulas(ByRef rng As Range) As Long
CountFormulas = rng.SpecialCells(xlCellTypeFormulas).Count
End Function
Public Sub CountFormulasFromSub()
Dim rng As Range
Dim res As Integer
Set rng = Sheet1.Range("a1:c7")
res = CountFormulas(rng)
End Sub
This isn't possible. The following link has the things that won't work inside of a UDF.
Here - http://support.microsoft.com/kb/170787
EDIT: A manual way of counting works though.
Public Function CountFormulas(rng As Range) As Integer
Dim i As Integer
Dim cell As Range
For Each cell In rng
If cell.HasFormula Then
i = i + 1
End If
Next
CountFormulas = i
End Function
Change Integer to Long if you think it will exceed 32767.
If I were to send worksheet.cells to the function, it would check all cells in the entire worksheet, quite many and quite slow. Although Excel 2007+ supports 16384*1048576 rows, only cells which have actually been used are loaded to memory. There would be no need to go through all the other 17 billion cells to check. The closest I could get to identifying these was using Worksheet.UsedRange to restrict an arbitrary range input. It is not perfect though, in cases when cells far apart have been used. E.g. if cells A1 and XFD1048576 contain data, the entire worksheet would be included in UsedRange. Any tips on how to restrict the range to actually used cells (merely two cells in the above example) would be greatly appreciated.
Utilizing UsedRange I built a function which I'll share in case anyone else can make use of it:
Public Function CountIfFormula(ByRef rng As Range, Optional ByVal matchStr As String) As Long
'Counts the number of cells containing a formula and optionally a specific string (matchStr) in the formula itself.
Dim i As Long
Dim isect As Range
'Restricts the range to used cells (checks only cells in memory)
Set isect = Application.Intersect(rng, rng.Parent.UsedRange)
For Each cell In isect
If cell.HasFormula Then
If InStr(1, cell.Formula, matchStr) Then i = i + 1
End If
Next
CountIfFormula = i
End Function
Use of the function:
Sub GetNrOfCells()
Dim i As Long
Dim ws As Worksheet
For Each ws In ThisWorkbook.Worksheets
i = i + CountIfFormula(ws.Cells, "=SUM(")
Next
'i will now contain the number of cells using the SUM function
End Sub
Best regards, and thank you for your reply.
Fossie

Resources