VBA Copy Paste string search - vba

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

Related

Excel VBA skips a lot of occurrences

I have a Workbook with 6 Sheets. I am walking through them with For Each. And the task is:
1) Walk though every cell with specified Range
2) If cell is not empty AND contains ONLY number THEN add to the end of the cell " мм". Otherwise SKIP this cell.
But in fact, script does it good only for first sheet (Worksheet). It does no changes to other sheets. I don't know why this happens. I think, that there is some error or mistake in the code, but I double-checked it and everything seems to be correct. Help me please :)
Sub SaveWorksheetsAsCsv()
Dim xWs As Worksheet
Dim xDir As String
Dim folder As FileDialog
Dim r As Range
Dim rr As Range
Dim rrrrrr As Range
Dim cell As Range
k = Cells(Rows.Count, "A").End(xlUp).Row
Set folder = Application.FileDialog(msoFileDialogFolderPicker)
If folder.Show <> -1 Then Exit Sub
xDir = folder.SelectedItems(1)
For Each xWs In Application.ActiveWorkbook.Worksheets
If xWs.Name Like "Worksheet" Then
Set r = Range("FA2:FA" & k)
For Each cell0 In r
If IsEmpty(cell0.Value) = False And IsNumeric(cell0.Value) = True Then
cell0.Value = cell0.Value & " мм"
End If
Next
'xWs.Columns(41).EntireColumn.Delete
End If
If xWs.Name Like "Worksheet 1" Then
Set rr = Range("AG2:AG" & k)
For Each cell1 In rr
If IsEmpty(cell1.Value) = False And IsNumeric(cell1.Value) Then
cell1.Value = cell1.Value & " мм"
End If
Next
'xWs.Columns(126).EntireColumn.Delete
End If
If xWs.Name Like "Worksheet 5" Then
Set rrrrrr = Range("FR2:FR" & k)
For Each cell5 In rrrrrr
If IsEmpty(cell5.Value) = False And IsNumeric(cell5.Value) Then
cell5.Value = cell5.Value & " мм"
End If
Next
End If
xWs.SaveAs xDir & "\" & xWs.Name, xlCSV, local:=True
Next
End Sub
These sets of statements need to be adjusted to correct sheet references. Current code will always look at active sheet and the range reference is not qualified.
Set r = Range("FA2:FA" & k)
Set r = xWs.Range("FA2:FA" & k)
You can shorten-up and utilize your code a lot.
First, your k = Cells(Rows.Count, "A").End(xlUp).Row trying to get the last row, needs to be inside the For Each xWs In Application.ActiveWorkbook.Worksheets , since the last row will be different for each worksheet.
Second, instead of multiple Ifs, you can use Select Case.
Third, there is no need to have 3 different objects for Range, like r, rr, and rrr. The same goes for cell0, cell1 and cell5, you can use just one r and cell.
The only thing different inside your If (my Select Case) is the range you set r. The rest, looping through r.Cells is the same for all 3 criterias, so you can take this part outside the loop, and have it only once.
Modifed Code
Option Explicit
Sub SaveWorksheetsAsCsv()
Dim xWs As Worksheet
Dim xDir As String
Dim folder As FileDialog
Dim r As Range
Dim cell As Range
Dim k As Long
Set folder = Application.FileDialog(msoFileDialogFolderPicker)
If folder.Show <> -1 Then Exit Sub
xDir = folder.SelectedItems(1)
For Each xWs In ThisWorkbook.Worksheets ' it's safer to use ThisWorkbook is you reffer to the worksheets inside the workbook which thid code resides
With xWs
' getting the last row needs to be inside the loop
k = .Cells(.rows.Count, "A").End(xlUp).Row
Set r = Nothing ' reset Range Object
Select Case .Name
Case "Worksheet"
Set r = .Range("FA2:FA" & k)
'xWs.Columns(41).EntireColumn.Delete
Case "Worksheet 1"
Set r = .Range("AG2:AG" & k)
'xWs.Columns(126).EntireColumn.Delete
Case "Worksheet 5"
Set r = .Range("FR2:FR" & k)
End Select
' check if r is not nothing (it passed one of the 3 Cases in the above select case)
If Not r Is Nothing Then
For Each cell In r
If IsEmpty(cell.Value) = False And IsNumeric(cell.Value) Then
cell.Value = cell.Value & " мм"
End If
Next cell
End If
.SaveAs xDir & "\" & .Name, xlCSV, Local:=True
End With
Next xWs
End Sub

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

Search Column Header Label Values

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

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

how to get more than one cell address out of `find` method in excel vba

This little macro returns the first cell address where the zip code matches. I need all of the cells where the zip code matches. Do I loop through somehow or does the find method offer more than one answer?
Sub practiceFind()
Dim wsVendor As Worksheet: Set wsVendor = Worksheets("vendorOutput.csv")
Dim zipColumnVendor As Integer: zipColumnVendor = 5
Dim vendorRows As Integer: vendorRows = wsVendor.Cells(Rows.Count, 1).End(xlUp).Row ' less 1 for label row
Dim searchRange As Range
Set searchRange = Worksheets("vendorOutput.csv").Range(Cells(2, zipColumnVendor), Cells(vendorRows, zipColumnVendor))
Dim x As Range
Set x = searchRange.Find(what:="10514", LookIn:=xlValues, LookAt:=xlPart)
MsgBox x.Address
End Sub
You can call FindNext on searchRange with x to get the next match.
Example:
Sub practiceFind()
Dim wsVendor As Worksheet: Set wsVendor = Worksheets("vendorOutput.csv")
Dim zipColumnVendor As Integer: zipColumnVendor = 5
Dim vendorRows As Integer: vendorRows = wsVendor.Cells(Rows.Count, 1).End(xlUp).Row ' less 1 for label row
Dim x As Range
With Worksheets("vendorOutput.csv").Range(Cells(2, zipColumnVendor), Cells(vendorRows, zipColumnVendor))
Set x = .Find(what:="10514", LookIn:=xlValues, LookAt:=xlPart)
If Not x Is Nothing Then
firstAddress = x.Address
Do
MsgBox x.Address
Set x = .FindNext(x)
Loop While Not x Is Nothing And x.Address <> firstAddress
End If
End With
End Sub
Instead of a find, you could do a filter:
Set searchRange = Worksheets("vendorOutput.csv").Range(Cells(1, zipColumnVendor), Cells(vendorRows, zipColumnVendor))
With searchRange
.AutoFilter 1, 10514
On Error Resume Next
Set x = .Offset(1).Resize(.Rows.Count - 1).SpecialCells(xlCellTypeVisible)
On Error GoTo 0
.AutoFilter
End With
If Not x is Nothing Then MsgBox x.Address