Search Column Header Label Values - vba

Is it possible to search row 1 (headers) for a value defined by a table from another sheet? I need "FName" to be a column or range of values as opposed to a single cell.
Here is a sample of what I was able to get working so far:
FName = Workbooks("IntChk.xlsm").Worksheets("Data").Range("B3")
Set rngFound = Worksheets("File").Rows(1).Find(What:=FName, LookIn:=xlValues, LookAt:=xlWhole, _
SearchOrder:=xlByColumns, SearchDirection:=xlNext, MatchCase:=False)

After identifying the search term from another workbook, you want to locate one or more occurrences in row 1 of this workbook (...?) and record the columns that correspond to the match(es).
Option Explicit
Sub get_em_all()
Dim fName As String, addr As String
Dim rng As Range, fnd As Range
'get search criteria
fName = Workbooks("IntChk.xlsm").Worksheets("Data").Range("B3")
With ThisWorkbook '<~~ different from IntChk.xlsm...?
With .Worksheets("File").Rows(1)
'perform first search
Set fnd = .Rows(1).Find(What:=fName, MatchCase:=False, _
LookIn:=xlValues, LookAt:=xlWhole)
'was anything found
If Not fnd Is Nothing Then
'record the first find
Set rng = fnd
addr = rng.Address
'loop and collect results until we arrive at the first find
Do
Set rng = Union(rng, fnd)
Set fnd = .FindNext(after:=fnd)
Loop Until addr = fnd.Address
'expand the found cells from the first row to the columns within the current region
With .Parent.Cells(1, 1).CurrentRegion
Set rng = Intersect(rng.EntireColumn, .Cells)
End With
'report the address(es) of the cell(s) found
Debug.Print rng.Address(0, 0)
Else
Debug.Print 'nothing found"
End If
End With
End With
End Sub

edited to correct some "optimization" typos
I think you want to select from a "headers" row all the cells whose value is on another range
If that's your goal you could try the following
Option Explicit
Function GetRange(fnameRng As Range, dataRng As Range) As Range
Dim fName As String
'get search criteria
fName = GetJoinFromRange(fnameRng)
With dataRng
.Rows(1).Insert
With .Offset(-1).Resize(1)
.FormulaR1C1 = "=if(isnumber(search(""-"" & R2C & ""-"" ,""" & fName & """)),1,"""")"
.Value = .Value
Set GetRange = .SpecialCells(xlCellTypeConstants)).Offset(1)
End With
.Rows(1).Offset(-1).EntireRow.Delete
End With
End Function
Function GetJoinFromRange(rng As Range) As String
If rng.Rows.Count > 1 Then
GetJoinFromRange = "-" & Join(Application.Transpose(rng), "-") & "-"
Else
GetJoinFromRange = "-" & Join(rng, "-") & "-"
End If
End Function
that can be called by a "main" sub like follows
Option Explicit
Sub main()
Dim fnameRng As Range, dataRng As Range, rngFound As Range
Set fnameRng = Workbooks("IntChk.xlsm").Worksheets("Data").Range("B3:B6") '<== adapt it to your needs
Set dataRng = ThisWorkbook.Worksheets("File").Range("B1:I1000") '<== adapt it to your needs
Set rngFound = GetRange(fnameRng, dataRng)
End Sub

after a week of trial and error, I was able to create this code. it works well and its light.
sub IntChk
Dim i As Integer
Lastcol = 5
For i = 1 To 1
For j = 1 To Lastcol
MsgBox "Cell Value = " & Cells(j) & vbNewLine & "Column Number = " & j
For Each c In Workbooks("IntChk.xlsm").Worksheets("Data").Range("A1:A50")
If c.Value = Cells(j) Then
MsgBox "Match"
Match = "True"
End If
Next c
Next j
If Match = "True" Then
MsgBox "Yes, True!"
Else:
MsgBox "not true ;("
End If
Next I
end sub

Related

Excel VBA - search, offset, replace

I have a settings sheet with unique identifiers in column D and replacement values in column F. I need to:
loop through all serial numbers in sheet settingscolumn D
find the row with the same serial in sheet test column A
get the replacement value from settings column F
replace the data in column B on the test sheet, in the same row as the previously searched serial
sounds simple enough but I am getting a type mismatch error when defining the for and to statement with the code below.
Sub Replace_List()
Dim rList As Range, cell As Range, n As Long
Application.ScreenUpdating = False
With ThisWorkbook.Sheets("Settings")
Set rList = .Range("D4", .Range("D" & Rows.Count).End(xlUp))
End With
For Each cell In rList
For n = cell.Value To cell.Offset(0, 2).Value Step 1
ThisWorkbook.Sheets("test").Columns("B:B").Replace What:=n, _
Replacement:=cell.Offset(0, 2).Value, _
LookAt:=xlWhole
Next n
Next cell
Application.ScreenUpdating = True
MsgBox "Replaced all items from the list.", vbInformation, "Replacements Complete"
End Sub
Any pointers on what I am doing wrong here are appreciated.
Thanks,
A2k
EDIT
Screenshots below:
Settings - I am looking up the survey ID and want to replace the original date with the correct one
I believe you want to use Find to find each occurrence, and then replace the value using an Offset of the found location:
Sub Replace_List()
Dim rList As Range, cel As Range, n As Long
Dim fnd As Range
Dim fndFirst As String
Application.ScreenUpdating = False
With ThisWorkbook.Sheets("Settings")
Set rList = .Range("D4", .Range("D" & .Rows.Count).End(xlUp))
End With
For Each cel In rList
Set fnd = ThisWorkbook.Worksheets("test").Columns("A:A").Find(What:=cel.Value, LookAt:=xlWhole)
If Not fnd Is Nothing Then
fndFirst = fnd.Address
Do
fnd.Offset(0, 1).Value = cel.Offset(0, 2).Value
Set fnd = ThisWorkbook.Worksheets("test").Columns("A:A").FindNext(After:=fnd)
Loop While fnd.Address <> fndFirst
End If
Next
Application.ScreenUpdating = True
MsgBox "Replaced all items from the list.", vbInformation, "Replacements Complete"
End Sub

Excel Macro, Find All Duplicates in Column and see coorisponding value

Excel Macro that will do the following:
To Find All Duplicates in (ColumnA) and to see if (ColumnB) contains a certain value and run a code against that result.
How i would write the code if i could:
If (ColumnB) .value in that (group of duplicates_found) in any row is "R-".value then
Keep the row with "R-".value and delete the rest. Else if "R-".value not exist and "M-".value Exist, delete all duplicates except first "R-".value found.
Else
If duplicate group contains "R-".value more than once, keep first "R-".value row found and delete the rest
Endif
Continue to loop until all duplicates found and run through above code.
^^sorry if not making sense up there:
I guess we can select first group of duplicates and run check on it like described below.^^
in this group all would be deleted, except one row.
(in this group we could specify to keep first "R-".value found and delete rest)
(this group has a "R-".value so the "M-".value gets deleted.)
(this group has a "R-".value so the "M-".value gets deleted.)
Code I used once to delete all "M-".value(s), hoping to reverse to do above as described per a first group found and to continue:
Sub DeleteRowWithContents()
Dim rFnd As Range, dRng As Range, rFst As String, myList, ArrCnt As Long
myList = Array("M-")
For ArrCnt = LBound(myList) To UBound(myList)
With Range("B1:B" & Range("B" & Rows.Count).End(xlUp).Row)
Set rFnd = .Find(What:=myList(ArrCnt), _
LookIn:=xlValues, _
LookAt:=xlPart, _
SearchOrder:=xlByRows, _
SearchDirection:=xlNext, _
MatchCase:=True)
If Not rFnd Is Nothing Then
rFst = rFnd.Address
Do
If dRng Is Nothing Then
Set dRng = Range("A" & rFnd.Row)
Else
Set dRng = Union(dRng, Range("A" & rFnd.Row))
End If
Set rFnd = .FindNext(After:=rFnd)
Loop Until rFnd.Address = rFst
End If
Set rFnd = Nothing
End With
Next ArrCnt
If Not dRng Is Nothing Then dRng.EntireRow.Delete
End Sub
this code goes through column and finds duplicates and highlights them. Maybe this could be rewritten to highlight each duplicate a separate color?
Sub MarkDuplicates()
Dim iWarnColor As Integer
Dim rng As Range
Dim rngCell As Variant
Range(Range("A2"), Range("A2").End(xlDown)).Select ' area to check '
Set rng = Selection
iWarnColor = xlThemeColorAccent2
For Each rngCell In rng.Cells
vVal = rngCell.Text
If (WorksheetFunction.CountIf(rng, vVal) = 1) Then
rngCell.Interior.Pattern = xlNone
Else
rngCell.Interior.ColorIndex = iWarnColor
End If
Next rngCell
End Sub
this code Looks for colored cells a specific RGB color and selects them, maybe for each group that is colored differently select that color and do a function on it?
Sub SelectColoredCells()
Dim rCell As Range
Dim lColor As Long
Dim rColored As Range
'Select the color by name (8 possible)
'vbBlack, vbBlue, vbGreen, vbCyan,
'vbRed, vbMagenta, vbYellow, vbWhite
lColor = RGB(156, 0, 6)
'If you prefer, you can use the RGB function
'to specify a color
'Default was lColor = vbBlue
'lColor = RGB(0, 0, 255)
Set rColored = Nothing
For Each rCell In Selection
If rCell.Interior.Color = lColor Then
If rColored Is Nothing Then
Set rColored = rCell
Else
Set rColored = Union(rColored, rCell)
End If
End If
Next
If rColored Is Nothing Then
MsgBox "No cells match the color"
Else
rColored.Select
MsgBox "Selected cells match the color:" & _
vbCrLf & rColored.Address
End If
Set rCell = Nothing
Set rColored = Nothing
End Sub
This has had me tied down to the computer for a week now and i cant seem to resolve it.
Here's an answer, it's a complicated one, but I took the question as a challenge to improve my use of particular methods in VBA.
This goes through your cells and creates an array of the results as you like.
I was using numbers in my testing, so every time you see str(Key) you might just need to remove the str() function.
This results in printing the array to columns D:E rather than removing rows from your list. You could just clear columns A:B and then print to "A1:B" & dict.Count - that would have the same effect, essentially.
Sub test()
Dim dict As Object
Set dict = CreateObject("Scripting.Dictionary")
Dim lastrow As Integer
lastrow = Cells(Rows.Count, "A").End(xlUp).Row
Dim strA As String
For i = 1 To lastrow
strA = Cells(i, 1)
dict(strA) = 1
Next
Dim vResult() As Variant
ReDim vResult(dict.Count - 1, 1)
Dim x As Integer
x = 0
Dim strB As String
Dim strKey As String
For Each Key In dict.keys
vResult(x, 0) = Key
x = x + 1
For Each c In Range("A1:A" & lastrow)
strA = Str(c)
strB = c.Offset(0, 1).Value
If strA = Str(Key) Then
If Left(strB, 1) = "r" Then
vResult(x - 1, 1) = c.Offset(, 1)
GoTo label
End If
End If
Next
If vResult(x - 1, 1) = Empty Then
For Each c In Range("A1:A" & lastrow)
strA = Str(c)
If strA = Str(Key) Then
vResult(x - 1, 1) = c.Offset(, 1)
GoTo label
End If
Next
End If
label:
Next
Range("D1:E" & dict.Count).Value = vResult()
End Sub

Excel remove rows that have certain words in a column

I am new to excel scripting/macroing
I have a massive excel sheet and I need to remove some rows from it.
Any rows that have the words: cat, dog, horse, fish
Have tried something like this (as well as quite a few other atempts).
Sub DeleteRows()
Dim c As Range
Dim SrchRng
Set SrchRng = ActiveSheet.Range("A1", ActiveSheet.Range("A65536").End(xlUp))
Do
Set c = SrchRng.Find("dog","cat","horse","fish", LookIn:=xlValues)
If Not c Is Nothing Then c.EntireRow.Delete
Loop While Not c Is Nothing
End Sub
Some details (since I am unsure what information is truely needed).
These words would always be in the 2nd column (but it could be something like "The George fish" and it would need to remove that row.
Any help would be great, thanks!
I have accomplished this without the help of Macros or scripts.
In the Data tab select filter, then select filter by word containing.
Enter the words, then simply delete all the rows that appear.
Answering incase anyone else has this problem in the future!
This works.
Sub DeleteRows()
Dim ws As Worksheet
Dim lastRow As Long
Dim lookAt As Range, thisCell As Range, nextCell As Range
Dim keyWord(0 To 3) As String, targetColumn As String
Dim i As Integer
keyWord(0) = "cat"
keyWord(1) = "dog"
keyWord(2) = "horse"
keyWord(3) = "fish"
On Error GoTo Err
Set ws = Application.ActiveSheet
'get last populated row number
With ws
If WorksheetFunction.CountA(Cells) > 0 Then
lastRow = Cells.Find(what:="*", SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious).Row
End If
End With
'define the target column
targetColumn = "A"
Set lookAt = ActiveSheet.Range(targetColumn & "1:" & targetColumn & lastRow)
'for each item in keyWord array
For i = LBound(keyWord) To UBound(keyWord)
'look for that word throughout range
Set thisCell = lookAt.Find(what:=keyWord(i), LookIn:=xlValues, lookAt:=xlPart, SearchDirection:=xlNext)
If Not thisCell Is Nothing Then
Set nextCell = thisCell
Do
Set thisCell = lookAt.FindNext(After:=thisCell)
If Not thisCell Is Nothing Then
If InStr(1, thisCell.Text, keyWord(i)) Then
thisCell.ClearContents
End If
Else
Exit Do
End If
Loop
End If
Next i
' delete empty rows
On Error Resume Next
Range(targetColumn & "1:" & targetColumn & lastRow).Select
Selection.EntireRow.SpecialCells(xlBlanks).EntireRow.Delete
Err:
Exit Sub
End Sub

VBA Copy Paste string search

I can’t seem to figure out how to write a vba code that search’s through cells C10:G10 to find a match that equals cell A10, once found, copies range A14:A18 to the matched cell but below e.g F14:F18 (See Image)
Macro below
'Copy
Range("A14:A18").Select
Selection.Copy
'Paste
Range("F14:F18").Select
ActiveSheet.Paste!
Try this:
With Sheets("SheetName") ' Change to your actual sheet name
Dim r As Range: Set r = .Range("C10:G10").Find(.Range("A10").Value2, , , xlWhole)
If Not r Is Nothing Then r.Offset(4, 0).Resize(5).Value2 = .Range("A14:A18").Value2
End With
Range Object have Find Method to help you find values within your range.
The Range object that matches your search criteria is then returned.
To get your values to the correct location, simply use Offset and Resize Method.
Edit1: To answer OP's comment
To find formulas in Ranges, you need to set LookIn argument to xlFormulas.
Set r = .Range("C10:G10").Find(What:=.Range("A10").Formula, _
LookIn:=xlFormulas, _
LookAt:=xlWhole)
Above code find Ranges with exactly the same formula as Cell A10.
Dim RangeToSearch As Range
Dim ValueToSearch
Dim RangeToCopy As Range
Set RangeToSearch = ActiveSheet.Range("C10:G10")
Set RangeToCopy = ActiveSheet.Range("A14:A18")
ValueToSearch = ActiveSheet.Cells(10, "A").Value
For Each cell In RangeToSearch
If cell.Value = ValueToSearch Then
RangeToCopy.Select
Selection.Copy
Range(ActiveSheet.Cells(14, cell.Column), _
ActiveSheet.Cells(18, cell.Column)).Select
ActiveSheet.Paste
Application.CutCopyMode = False
Exit For
End If
Next cell
another additional variants
1.Using For each loop
Sub test()
Dim Cl As Range, x&
For Each Cl In [C10:G10]
If Cl.Value = [A10].Value Then
x = Cl.Column: Exit For
End If
Next Cl
If x = 0 Then
MsgBox "'" & [A10].Value & "' has not been found in range 'C10:G10'!"
Exit Sub
End If
Range(Cells(14, x), Cells(18, x)).Value = [A14:A18].Value
End Sub
2.Using Find method (already posted by L42, but a bit different)
Sub test2()
Dim Cl As Range, x&
On Error Resume Next
x = [C10:G10].Find([A10].Value2, , , xlWhole).Column
If Err.Number > 0 Then
MsgBox "'" & [A10].Value2 & "' has not been found in range 'C10:G10'!"
Exit Sub
End If
[A14:A18].Copy Range(Cells(14, x), Cells(18, x))
End Sub
3.Using WorksheetFunction.Match
Sub test2()
Dim Cl As Range, x&
On Error Resume Next
x = WorksheetFunction.Match([A10], [C10:G10], 0) + 2
If Err.Number > 0 Then
MsgBox "'" & [A10].Value2 & "' has not been found in range 'C10:G10'!"
Exit Sub
End If
[A14:A18].Copy Range(Cells(14, x), Cells(18, x))
End Sub
Here you go,
Sub DoIt()
Dim rng As Range, f As Range
Dim Fr As Range, Crng As Range
Set Fr = Range("A10")
Set Crng = Range("A14:A18")
Set rng = Range("C10:G19")
Set f = rng.Find(what:=Fr, lookat:=xlWhole)
If Not f Is Nothing Then
Crng.Copy Cells(14, f.Column)
Else: MsgBox "Not Found"
Exit Sub
End If
End Sub

Find all matches in workbook using Excel VBA

I am trying to write a VBA routine that will take a string, search a given Excel workbook, and return to me all possible matches.
I currently have an implementation that works, but it is extremely slow as it is a double for loop. Of course the built in Excel Find function is "optimized" to find a single match, but I would like it to return an array of initial matches that I can then apply further methods to.
I will post some pseudocode of what I have already
For all sheets in workbook
For all used rows in worksheet
If cell matches search string
do some stuff
end
end
end
As previously stated, this double for loop makes things run very slowly, so I am looking to get rid of this if possible. Any suggestions?
UPDATE
While the below answers would have improved my method, I ended up going with something slightly different as I needed to do multiple queries over and over.
I instead decided to loop through all rows in my document and create a dictionary containing a key for each unique row. The value this points to will then be a list of possible matches, so that when I query later, I can simply just check if it exists, and if so, just get a quick list of matches.
Basically just doing one initial sweep to store everything in a manageable structure, and then query that structure which can be done in O(1) time
Using the Range.Find method, as pointed out above, along with a loop for each worksheet in the workbook, is the fastest way to do this. The following, for example, locates the string "Question?" in each worksheet and replaces it with the string "Answered!".
Sub FindAndExecute()
Dim Sh As Worksheet
Dim Loc As Range
For Each Sh In ThisWorkbook.Worksheets
With Sh.UsedRange
Set Loc = .Cells.Find(What:="Question?")
If Not Loc Is Nothing Then
Do Until Loc Is Nothing
Loc.Value = "Answered!"
Set Loc = .FindNext(Loc)
Loop
End If
End With
Set Loc = Nothing
Next
End Sub
Based on Ahmed's answer, after some cleaning up and generalization, including the other "Find" parameters, so we can use this function in any situation:
'Uses Range.Find to get a range of all find results within a worksheet
' Same as Find All from search dialog box
'
Function FindAll(rng As Range, What As Variant, Optional LookIn As XlFindLookIn = xlValues, Optional LookAt As XlLookAt = xlWhole, Optional SearchOrder As XlSearchOrder = xlByColumns, Optional SearchDirection As XlSearchDirection = xlNext, Optional MatchCase As Boolean = False, Optional MatchByte As Boolean = False, Optional SearchFormat As Boolean = False) As Range
Dim SearchResult As Range
Dim firstMatch As String
With rng
Set SearchResult = .Find(What, , LookIn, LookAt, SearchOrder, SearchDirection, MatchCase, MatchByte, SearchFormat)
If Not SearchResult Is Nothing Then
firstMatch = SearchResult.Address
Do
If FindAll Is Nothing Then
Set FindAll = SearchResult
Else
Set FindAll = Union(FindAll, SearchResult)
End If
Set SearchResult = .FindNext(SearchResult)
Loop While Not SearchResult Is Nothing And SearchResult.Address <> firstMatch
End If
End With
End Function
Usage is the same as native .Find, but here is a usage example as requested:
Sub test()
Dim SearchRange As Range, SearchResults As Range, rng As Range
Set SearchRange = MyWorksheet.UsedRange
Set SearchResults = FindAll(SearchRange, "Search this")
If SearchResults Is Nothing Then
'No match found
Else
For Each rng In SearchResults
'Loop for each match
Next
End If
End Sub
Function GetSearchArray(strSearch)
Dim strResults As String
Dim SHT As Worksheet
Dim rFND As Range
Dim sFirstAddress
For Each SHT In ThisWorkbook.Worksheets
Set rFND = Nothing
With SHT.UsedRange
Set rFND = .Cells.Find(What:=strSearch, LookIn:=xlValues, LookAt:=xlPart, SearchOrder:=xlRows, SearchDirection:=xlNext, MatchCase:=False)
If Not rFND Is Nothing Then
sFirstAddress = rFND.Address
Do
If strResults = vbNullString Then
strResults = "Worksheet(" & SHT.Index & ").Range(" & Chr(34) & rFND.Address & Chr(34) & ")"
Else
strResults = strResults & "|" & "Worksheet(" & SHT.Index & ").Range(" & Chr(34) & rFND.Address & Chr(34) & ")"
End If
Set rFND = .FindNext(rFND)
Loop While Not rFND Is Nothing And rFND.Address <> sFirstAddress
End If
End With
Next
If strResults = vbNullString Then
GetSearchArray = Null
ElseIf InStr(1, strResults, "|", 1) = 0 Then
GetSearchArray = Array(strResults)
Else
GetSearchArray = Split(strResults, "|")
End If
End Function
Sub test2()
For Each X In GetSearchArray("1")
Debug.Print X
Next
End Sub
Careful when doing a Find Loop that you don't get yourself into an infinite loop... Reference the first found cell address and compare after each "FindNext" statement to make sure it hasn't returned back to the first initially found cell.
You may use the Range.Find method:
http://msdn.microsoft.com/en-us/library/office/ff839746.aspx
This will get you the first cell which contains the search string. By repeating this with setting the "After" argument to the next cell you will get all other occurrences until you are back at the first occurrence.
This will likely be much faster.
Based on the idea of B Hart's answer, here's my version of a function that searches for a value in a range, and returns all found ranges (cells):
Function FindAll(ByVal rng As Range, ByVal searchTxt As String) As Range
Dim foundCell As Range
Dim firstAddress
Dim rResult As Range
With rng
Set foundCell = .Find(What:=searchTxt, _
After:=.Cells(.Cells.Count), _
LookIn:=xlValues, _
LookAt:=xlWhole, _
SearchOrder:=xlByRows, _
SearchDirection:=xlNext, _
MatchCase:=False)
If Not foundCell Is Nothing Then
firstAddress = foundCell.Address
Do
If rResult Is Nothing Then
Set rResult = foundCell
Else
Set rResult = Union(rResult, foundCell)
End If
Set foundCell = .FindNext(foundCell)
Loop While Not foundCell Is Nothing And foundCell.Address <> firstAddress
End If
End With
Set FindAll = rResult
End Function
To search for a value in the whole workbook:
Dim wSh As Worksheet
Dim foundCells As Range
For Each wSh In ThisWorkbook.Worksheets
Set foundCells = FindAll(wSh.UsedRange, "YourSearchString")
If Not foundCells Is Nothing Then
Debug.Print ("Results in sheet '" & wSh.Name & "':")
Dim cell As Range
For Each cell In foundCells
Debug.Print ("The value has been found in cell: " & cell.Address)
Next
End If
Next
You can read the data into an array. From there you can do the match in memory, instead of reading one cell at a time.
Pass cell contents into VBA Array
Below code avoids creating infinite loop. Assume XYZ is the string which we are looking for in the workbook.
Private Sub CommandButton1_Click()
Dim Sh As Worksheet, myCounter
Dim Loc As Range
For Each Sh In ThisWorkbook.Worksheets
With Sh.UsedRange
Set Loc = .Cells.Find(What:="XYZ")
If Not Loc Is Nothing Then
MsgBox ("Value is found in " & Sh.Name)
myCounter = 1
Set Loc = .FindNext(Loc)
End If
End With
Next
If myCounter = 0 Then
MsgBox ("Value not present in this worrkbook")
End If
End Sub