Excel VBA Macros: Find/Match cells from one sheet to a column in another - vba

Ok, so I have a workbook with multiple sheets. The Worksheets are named:
Inputs
Outputs
Hardware
Input and output are serial IDs matched to actualy IP Addresses.
Input 1 : 192.168.0.1
input 2 : 192.168.0.2
... etc
Hardware has 3 columns. The first has Devices, 2nd column which has the Input Serial IDs and the 3rd of Output Serial IDs.
Toaster : Input 1 : Output 3
Blender : Input 2 : Output 2
...etc
Now, normally, I'd be using Vlookup(A1,Inputs!A:B,2) and Vlookup(A1,Outputs!A:B,2), but I have to incorporate this into the VBA macro we have and I have no idea how.
Sub TrackHardware()
'~~~~~~~~~~~~~~~~~~~~~
'Activating Device
'~~~~~~~~~~~~~~~~~~~~~
currentOutputRow = 2
Dim test As String
For currentRow = 2 To 32768 'The last row of your data
'For Loop to go through contents of Hardware individually
If Not (IsEmpty(Worksheets("Hardware").Range("A" & currentRow).Value)) Then
'To Skip the empty cells
HWID=Worksheets("Hardware").Range("a" & currentvalue).Value
'HWID is the search term coming from Sheet:'Hardware'
Desc=Worksheets("Hardware").Range("D" & currentvalue).Value
'Desc is the Plain Text description coming from Sheet:'Hardware'
inputrow={Match pseudocode that didn't work(HWID, "Inputs", Range:= "A:B", 2) }
outputrow={Match pseudocode that didn't work(HWID, "Outputs", Range:= "A:B", 2) }
'trying to find the row # of search term in Sheets 'Input' and 'Output'
Worksheets("Inputs").Range("C" & inputrow).Value = Desc
Worksheets("Outputs").Range("C" & outputrow).Value = Desc
'Pastes The Device Description to Input and Output Sheets
End If
Next currentRow
'And on to the next line in 'Hardware'
End Sub
I'd also like to account for Errors like 2 devices on the same Input/Output or a Blank cell, but I think I can figure those out myself. This Find function is what's really giving me a lot of trouble.

First, there seems to be a problem if you are not able to call on the Application.Match function. I am not sure why that would be missing, but I know there are some "limited" versions of Office/Excel which do not have full VBA functionality. I am not sure if that is the case with your installation.
Now, on to your problem though...
To use the Application.Match function:
The Match function takes a single row or single column range input. You are attempting to pass in the range "A:B", which will always raise an error. Change to a single row/column range instead.
Further, 2 is not an option for the third argument, which can be either -1 (less than), 0 or False (exact), or 1 (greater than). I'm not sure this alone will raise an error, but you should fix it anyways.
inputRow = Application.Match(HWID, Worksheets("Inputs").Range("A:A"), False)
If an exact match cannot be found, it will raise an error, which you can trap like so:
inputRow = Application.Match(HWID, Worksheets("Inputs").Range("A:A"), False)
If IsError(inputRow) Then
'Do something like:
MsgBox HWID & " not found!", vbInformation
Exit Sub
End If
NOTE If you actually need to check both columns, then you can either double up on the Match function, or use the range .Find method instead.
Dim foundRange as Range
Set foundRange = Range("A:B").Find(HWID)
If Not foundRange Is Nothing Then
inputRow = foundRange.Row
Else
MsgBox HWID & " not found!", vbInformation
End If
Handling errors with WorksheetFunction.Match
Error trapping for Application.WorksheetFunction.Match should be something like:
inputRow = Empty
On Error Resume Next
inputRow = Application.WorksheetFunction.Match(HWID, Worksheets("Inputs").Range("A:A"), False)
If Err.Number <> 0 Then
MsgBox "Match not found", vbInformation
Exit Sub
End If
On Error GoTo 0

Related

VBA User form gives warning if duplicate is found

I think I need to try and make this question easier. So here goes;
I am creating a User form in Excel that will act as a data capture form.
In this form I have a Textbox called PolBX In this a is placed and at submission data in PolBX is copied into the "G" column using this code
Cells(emptyRow, 7).Value = PolBX.Value. This works great.
I discovered that there may be instances where the User may accidently use the same Unique Id number twice. so I am trying to find out how to code it that after the User has entered the Unique Id number it would check for that string (Consists of letters and numbers). if it finds the string already in the 7th column(G) it must say something like
"Policy number already Used, please try again"
I am thinking I will need to use the following subroutine
Private Sub PolBX_AfterUpdate()
End Sub
Can some please assist with creating this code...
Also can you please explain what you are doing as I started VBA about a week ago
You can add the following code to search for your policy number, and if nothing found then PolLookup = Nothing.
Option Explicit
Sub Test()
On Error GoTo ErrHandler
Dim ws As Worksheet, PolLookup As Range, LookupRng As Range
Set ws = ThisWorkbook.Worksheets(1)
'This is the range you want to search, it can be a long range
'or it can be a single cell.
Set LookupRng = ws.Range("A:A")
'Range.Find is looking for your value in the range you specified above
Set PolLookup = LookupRng.Find("YourLookupValue")
'PolLookup = Nothing if it didn't find a match, so we want to use
'If <NOT> Nothing, because this suggests .Find found your value
If Not PolLookup Is Nothing Then
Err.Raise vbObjectError + 0 'Whatever error you want to throw for finding a match
End If
'Exit before you reach the ErrHandler
Exit Sub
ErrHandler:
If Err.Number = vbObjectError + 0 Then
'Handle your error. Do you want to stop completely? Or have the
'User enter a new value?
End If
End Sub
Basically, after your user enters their value in your UserForm, just make a call to this Sub to do a quick lookup.
Playing around I discovered a Much easier way! I included a Button with he following code attached
Private Sub CommandButton8_Click()
Search = PolBX.Text
Set FoundCell = Worksheets("sheet1").Columns(7).Find(Search,LookIn:=xlValues, lookat:=xlWhole)
If FoundCell Is Nothing Then
MsgBox "No duplicates found"
Else
MsgBox "This policy has already been Assessed" & "Please assess a different case"
PolBX.Value = ""
End If

Find invalid data validation cells

Description:
I have created a custom dropdown data validation list where I can choose among several values. These values on the dropdown list changes as I need (are defined in a worksheet column X).
Problem:
My problem occurs when I choose one of those values, let say Y, from the dropdown list and then I update the data validation by removing the last inserted value (deleted the Y value from column X). By doing this the value Y present in the worksheet is no longer valid so I would like to know if there is a way to obtain a list (array or string) of cells with the invalid data.
What I have done/thought so far:
I have searched in several sites and read similar questions but I cannot find anything usefull. I thought about looping all the cells and check if the value is valid but since I have a huge amount of data I think that it is not the best approach.
Since Excel already mark these invalid data with a red circle maybe it could be possible to get the address of those marked cells?
Thanks in advance!
The correct way to obtain the invalid cells in a worksheet is using Cells.SpecialCells(xlCellTypeAllValidation).
By using some information present in Microsoft KB213773 (Q213773) - "How to create data validation circles for printing in Excel" a similar Sub can be used to loop all invalid cells and then change their values (or mark them to future edit).
Sub CorrectInvalidValues()
Dim data_range As Range
Dim invalid_cell As Range
Dim count As Integer: count = 0
Dim nr_invalid As Integer: nr_invalid = 0
Dim new_value As String
'If an error occurs run the error handler and end the procedure
On Error GoTo errhandler
Set data_range = Cells.SpecialCells(xlCellTypeAllValidation)
On Error GoTo 0
' Loop through each cell that has data validation and gets the number of invalid cells
For Each invalid_cell In data_range
If Not invalid_cell.Validation.Value Then
nr_invalid = nr_invalid + 1
End If
Next
' Editing each value
For Each invalid_cell In data_range
If Not invalid_cell.Validation.Value Then
count = count + 1
Application.Goto reference:=invalid_cell, Scroll:=True
new_value = Application.InputBox("Please insert a correct value.", "Invalid Data " & count & "/" & nr_invalid)
If Not (new_value = "False") Then
invalid_cell.Interior.ColorIndex = 0
invalid_cell.Value = new_value
Else
invalid_cell.Interior.Color = RGB(255, 0, 0)
invalid_cell.Value = "<PLEASE EDIT>"
End If
End If
Next
Exit Sub
errhandler:
MsgBox "There are no cells with data validation on this sheet."
End Sub

VBA inputbox input validation issue for excel

Excel requirement: User to enter a number via an input dialog. Rows containing that number in the designated (hard-coded) column of the current selection are to be deleted.
Regarding input validation, I have not come up with or found a solution that covers all these issues:
Empty input is not to be accepted. (Some variations I've tried treat it as zero.) Preferrably an error would be shown, but ok to treat like the cancel button if there's no better way.
Cancel button is to cancel dialog without further messages. (In the code below, it results in "invalid input."
A zero value entry must be permitted. (Not treated as cancel like in some of my attempts)
A zero value when there is no zero to match must show "Nothing to delete."
A row is not to be deleted if the cell is empty. ie. Do not match zero with an empty cell.
I've tried variations of code, but at least one issue remains in each. The code below is modified from my original post. It's my best solution so far. The only issue is that cancel results in "No value entered."
Thanks.
Sub DeleteRows()
On Error GoTo InvalidInput
Dim InputRng As Range
Dim DeleteRng As Range
Dim DeleteNum As Double
Dim DeleteV As Variant
Dim CountDelete As Long
Dim CountAreas As Long
Dim Str As String
Set InputRng = Application.Selection.Rows
DeleteV = Application.InputBox("Match Value", "Delete Rows")
'TODO Ultimate solution: Check for cancel so as not to result in "No value entered".
'NOTE: If DeleteV = Empty is true upon cancel
'NOTE this unwantingly matches zero: If DeleteV = False Then Exit Sub
'Check something was entered
If DeleteV = Empty Then
MsgBox ("No value entered.")
Exit Sub
End If
DeleteNum = CDbl(DeleteV)
'Accumulate what is to be deleted
For Each r In InputRng.Rows
'Only if cell is a number
If Not IsEmpty(r.Cells(3)) Then
If IsNumeric(r.Cells(3).Value) Then
If r.Cells(3).Value = DeleteNum Then
If DeleteRng Is Nothing Then
Set DeleteRng = r
Else
Set DeleteRng = Application.Union(DeleteRng, r)
End If
End If
End If
End If
Next
If DeleteRng Is Nothing Then
MsgBox ("Nothing to delete.")
Exit Sub
End If
CountDelete = 0
'The range may contain separated areas,
'so loop through each area to accumulate the total count
For Each a In DeleteRng.Areas
CountDelete = CountDelete + a.Rows.Count
Next a
If CountDelete > 1 Then
Str = " rows."
Else
Str = " row."
End If
'Confirm delete
If MsgBox("Confirm delete " & CountDelete & Str, vbYesNo) = vbYes Then
DeleteRng.EntireRow.Delete
End If
Exit Sub
InvalidInput:
MsgBox ("Invalid input.")
End Sub
Try it with Type 1
Syntax:
expression.InputBox(Prompt, Title, Default, Left, Top, HelpFile, HelpContextID, Type)
Type:
The following table lists the values that can be passed in the Type argument. Can be one or a sum of the values. For example, for an input box that can accept both text and numbers, set Type to 1 + 2.
Value Meaning
0 A formula
1 A number
2 Text (a string)
4 A logical value (True or False)
8 A cell reference, as a Range object
16 An error value, such as #N/A
64 An array of values
DeleteV = Application.InputBox("Match Value", "Delete Rows", Type:=1)
Sub Sample()
'~~> Only valid numbers are allowed including 0
'~~> Empty input will not to be accepted.
DeleteV = Application.InputBox("Match Value", "Delete Rows", Type:=1)
'~~> If user presses Cancel
If DeleteV = "False" Then Exit Sub
'~~> If user enters a number else it won't execute this
If DeleteV >= 0 Then
'
'~~> Rest of your code
'
End If
End Sub
Your Points which are taken care of
Empty input is not to be accepted.
Cancel button is to cancel dialog without further messages.
A zero value entry must be permitted. (Not treated as cancel like in some of my attempts)
A zero value when there is no zero to match must show "Nothing to delete." Can be handled inside the If-EndIf
A row is not to be deleted if the cell is empty. ie. Do not match zero with an empty cell. Can be handled inside the If-EndIf

excel - find and highlight text from one sheet on second sheet

I am getting killed by excel, I'm not 100% sure it can do exactly what I'm needing. I've tried various functions and can come close, but none are perfect. I've uploaded a spreadsheet as an example. I have a sheet of mailboxes, followed by a cell with the users who have access to the mailbox. The cell has anywhere from 0 to 5 users separated by commas. The second sheet has a list of users. What I need is a way to parse out the first sheet, either highlight on the first sheet, or copy to another sheet; all the mailboxes that all the associated users match in the second cell appear on the second sheet.
The real world sheet I have has over 2500 mailboxes with as many as 205 (as few as 0) associated users, so I desperately need a way to mechanically filter the sheet. I'm trying to filter the mailboxes that all the associated users are present on a second sheet.
I've tried using vlookup, index/match and a few others, and what seems to trip it up is having the comma separation. Using ""& cell_i'm_looking_for &"" returns nothing so I'm guessing I need to try something else. I also have the sheet with all the users in separate cells.
I downloaded your sheet and created a module with the following function inside of it:
Function mymailboxes(who As Range, lookup As Range)
Dim myRow As Range
For Each myRow In lookup
If InStr(myRow.Cells(1, 2).Value, who.Value) > 0 Then
myReturn = myReturn & "," & myRow.Cells(1, 1).Value
End If
Next
'cut off the first ,
myReturn = Right(myReturn, Len(myReturn) - 1)
mymailboxes = myReturn
End Function
Then on Sheet 2, Cell E2 I gave the following formula: =mymailboxes(A2, Sheet1!A1:B50) which gave me the following results: mailbox1,mailbox2,mailbox4,mailbox8,mailbox12,mailbox17,mailbox21,mailbox25,mailbox28,mailbox34,mailbox39,mailbox41,mailbox42,mailbox44,mailbox49,mailbox50
I hope this helps.
To get a list of invalid users the following function will help.
Function get_invalid_users(users As Range, validusers As Range)
Dim myRow As Range
myusers = Split(users, ",")
myReturn = ""
For Each user In myusers
is_valid_user = False
'Guilty until proven innocent
For Each myRow In validusers
If myRow.Cells(1, 1).Value = user Then
is_valid_user = True
'Proven innocent, break out of the loop, no double jeopardy.
Exit For
End If
Next
If is_valid_user = False Then
myReturn = myReturn & "," & user
End If
Next
If Len(myReturn) > 0 Then
myReturn = Right(myReturn, Len(myReturn) - 1)
Else
myReturn = ""
End If
get_invalid_users = myReturn
End Function
Sheet 1, Cell C2 with formula: =get_invalid_users(B2, Sheet2!$A$1:$A$3) returned zx1234

Error 9: Subscript out of range

I have a problem in excel Vba when I try to run this code, I have an error of subscript out of range:
Private Sub UserForm_Initialize()
n_users = Worksheets(Aux).Range("C1").Value
Debug.Print Worksheets(Aux).Range("B1:B" & n_users).Value
ListBox1.RowSource = Worksheets(Aux).Range("B1:B" & n_users).Value
ComboBox1.RowSource = Worksheets(Aux).Range("B1:B" & n_users).Value
ComboBox2.RowSource = Worksheets(Aux).Range("B1:B" & n_users).Value
End Sub
And Debug.Print works well, so the only problem is in Range("B1:B" & n_users).Value.
If the name of your sheet is "Aux", change each Worksheets(Aux) reference to Worksheets("Aux"). Unless you make Aux a string variable, for example:
Dim Aux As String
Aux = "YourWorksheetName"
n_users = Worksheets(Aux).Range(C1).Value
you must use quatations around sheet references.
Firstly, unless you have Aux defined somewhere in the actual code, this will not work. The sheet-name reference must be a string value, not an empty variable (which ARich explains in his answer).
Second, the way in which you are trying to populate the rowsource value is incorrect. The rowsource property of a combobox is set using a string value that references the target range. By this I mean the same string value you would use in an excel formula to reference a cell in another sheet. For instance, if your worksheet is named "Aux" then this would be your code:
ComboBox1.RowSource = "Aux!B1:B" & n_users
I think you can also use named ranges. This link explains it a little.
I can't see how you can get an Error 9 on that line. As others have pointed out repeatedly, the place you'll get it is if the variable Aux doesn't have a string value representing the name of a worksheet. That aside, I'm afraid that there is a LOT wrong with that code. See the comments in the below revision of it, which as near as I can figure is what you're trying to get to:
Private Sub UserForm_Initialize()
'See below re this.
aux = "Sheet2"
'You should always use error handling.
On Error GoTo ErrorHandler
'As others have pointed out, THIS is where you'll get a
'subscript out of range if you don't have "aux" defined previously.
'I'm also not a fan of NOT using Option Explicit, which
'would force you to declare exactly what n_users is.
'(And if you DO have it declared elsewhere, I'm not a fan of using
'public variables when module level ones will do, or module
'level ones when local will do.)
n_users = Worksheets(aux).Range("C1").Value
'Now, I would assume that C1 contains a value giving the number of
'rows in the range in column B. However this:
'*****Debug.Print Worksheets(aux).Range("B1:B" & n_users).Value
'will only work for the unique case where that value is 1.
'Why? Because CELLS have values. Multi-cell ranges, as a whole,
'do not have single values. So let's get rid of that.
'Have you consulted the online Help (woeful though
'it is in current versions) about what the RowSource property
'actually accepts? It is a STRING, which should be the address
'of the relevant range. So again, unless
'Range("B1:B" & n_users) is a SINGLE CELL that contains such a string
'(in which case there's no point having n_users as a variable)
'this will fail as well when you get to it. Let's get rid of it.
'****ListBox1.RowSource = Worksheets(aux).Range("B1:B" & n_users).Value
'I presume that this is just playing around so we'll
'ignore these for the moment.
'ComboBox1.RowSource = Worksheets(aux).Range("B1:B" & n_users).Value
'ComboBox2.RowSource = Worksheets(aux).Range("B1:B" & n_users).Value
'This should get you what you want. I'm assigning to
'variables just for clarity; you can skip that if you want.
Dim l_UsersValue As Long
Dim s_Address As String
l_UsersValue = 0
s_Address = ""
'Try to get the n_users value and test for validity
On Error Resume Next
l_UsersValue = Worksheets(aux).Range("C1").Value
On Error GoTo ErrorHandler
l_UsersValue = CLng(l_UsersValue)
If l_UsersValue < 1 Or l_UsersValue > Worksheets(aux).Rows.Count Then
Err.Raise vbObjectError + 20000, , "User number range is outside acceptable boundaries. " _
& "It must be from 1 to the number of rows on the sheet."
End If
'Returns the cell address
s_Address = Worksheets(aux).Range("B1:B" & n_users).Address
'Add the sheet name to qualify the range address
s_Address = aux & "!" & s_Address
'And now that we have a string representing the address, we can assign it.
ListBox1.RowSource = s_Address
ExitPoint:
Exit Sub
ErrorHandler:
MsgBox "Error: " & Err.Description
Resume ExitPoint
End Sub