Inserting Range into Array in VBA for iteration - vba

I am facing some issues with VBA. Let me explain what I am trying to achieve. I have 2 sheets in 1 workbook. They are labelled "Sheet1" and "Sheet2."
In "Sheet1," there are 100 rows and 100 columns. In column A, it is filled with eg: SUBJ001 all the way to SUBJ100. In "Sheet2," there is only 1 Column A, with a range of rows. Eg: "SUBJ003, SUBJ033, SUBJ45." What I am trying to achieve is to use my mouse, highlight the column A in "Sheet2," and compare each individual cell with the cells in column A. Should there be a match, it will copy the entire row and paste them in a new sheet that the macro creates in the same workbook. However, i am experiencing an out of range error at Set Rng =.Find(What:=Arr(I), ... Thanks!
Sub Copy_To_Another_Sheet_1()
Dim FirstAddress As String
Dim MyArr As Variant
Dim Rng As Range
Dim Rcount As Long
Dim I As Long
Dim NewSh As Worksheet
With Application
.ScreenUpdating = False
.EnableEvents = False
End With
Set Rng = Application.InputBox("Select target range with the mouse", Type:=8)
MyArr = Rng
Set NewSh = Worksheets.Add
With Sheets("Sheet1").Range("A:A")
Rcount = 0
For I = LBound(MyArr) To UBound(MyArr)
Set Rng = .Find(What:=MyArr(I), _
After:=.Cells(.Cells.Count), _
LookIn:=xlFormulas, _
LookAt:=xlPart, _
SearchOrder:=xlByRows, _
SearchDirection:=xlNext, _
MatchCase:=False)
If Not Rng Is Nothing Then
FirstAddress = Rng.Address
Do
Rcount = Rcount + 1
Rng.EntireRow.Copy NewSh.Range("A" & Rcount)
' Use this if you only want to copy the value
' NewSh.Range("A" & Rcount).Value = Rng.Value
Set Rng = .FindNext(Rng)
Loop While Not Rng Is Nothing And Rng.Address <> FirstAddress
End If
Next I
End With
With Application
.ScreenUpdating = True
.EnableEvents = True
End With
End Sub

MyArr = Rng is setting MyArr to be a two-dimensional array where the first rank corresponds to the rows in Rng and the second rank corresponds to the columns in Rng.
Assuming you only have one column in Rng, then your Find statement should refer to the values in that first column using MyArr(I, 1), i.e.
Set Rng = .Find(What:=MyArr(I, 1), _
After:=.Cells(.Cells.Count), _
LookIn:=xlFormulas, _
LookAt:=xlPart, _
SearchOrder:=xlByRows, _
SearchDirection:=xlNext, _
MatchCase:=False)

Related

VBA - Search value in a column and reorder them in a new column

in a column I have multiple values (Vat numbers) but not in consecutive rows, for example in A1, A5, A8,...
I need to copy each value from column A and paste them in a new column but reordered in consecutive rows, for example B1,B2,B3,...
The following macro does this job but only if in the value it is included the symbol "#" because it uses the function "Array" (if I replace # with numbers as 1,2,3,4,5,6,7,8,9, I get the same VAT number multiple times and I do not require it).
Sub Copy_To_Another_Sheet_1()
Dim FirstAddress As String
Dim MyArr As Variant
Dim Rng As Range
Dim Rcount As Long
Dim I As Long
Dim NewSh As Worksheet
With Application
.ScreenUpdating = False
.EnableEvents = False
End With
MyArr = Array("#")
Set NewSh = Sheets("Sheet2")
With Sheets("Sheet2").Range("A1:A100")
Rcount = 0
For I = LBound(MyArr) To UBound(MyArr)
'If you use LookIn:=xlValues it will also work with a
'formula cell that evaluates to "#"
'Note : I use xlPart in this example and not xlWhole 'MyArr(I)
Set Rng = .Find(What:=MyArr(I), _
After:=.Cells(.Cells.Count), _
LookIn:=xlFormulas, _
LookAt:=xlPart, _
SearchOrder:=xlByRows, _
SearchDirection:=xlNext, _
MatchCase:=False)
If Not Rng Is Nothing Then
FirstAddress = Rng.Address
Do
Rcount = Rcount + 1
NewSh.Range("A" & Rcount).Value = Rng.Value
' Use this if you only want to copy the value
' NewSh.Range("A" & Rcount).Value = Rng.Value
Set Rng = .FindNext(Rng)
Loop While Not Rng Is Nothing And Rng.Address <> FirstAddress
End If
Next I
End With
With Application
.ScreenUpdating = True
.EnableEvents = True
End With
End Sub
Thanks
Marco

Editing code to copy the whole row

I found some code that does most of what I need it to do but I need to copy the whole row where it finds the occurrence of the variable, not just the cell in which it finds the occurrence. Any thoughts on how to edit it to copy the cell in which it found the variable to the last cell in the row containing data?
Sub Copy_To_Another_Sheet_1()
Dim FirstAddress As String
Dim MyArr As Variant
Dim Rng As Range
Dim Rcount As Long
Dim I As Long
Dim NewSh As Worksheet
Dim Initiatives As Worksheet
Set Initiatives = ThisWorkbook.Worksheets("Initiatives")
With Application
.ScreenUpdating = False
.EnableEvents = False
End With
MyArr = Array("Components")
Set NewSh = Worksheets.Add
With Initiatives.Range("B3:B500")
Rcount = 0
For I = LBound(MyArr) To UBound(MyArr)
Set Rng = .Find(What:=MyArr(I), _
After:=.Cells(.Cells.Count), _
LookIn:=xlFormulas, _
LookAt:=xlPart, _
SearchOrder:=xlByRows, _
SearchDirection:=xlNext, _
MatchCase:=False)
If Not Rng Is Nothing Then
FirstAddress = Rng.Address
Do
Rcount = Rcount + 1
Rng.Copy NewSh.Range("A" & Rcount)
Set Rng = .FindNext(Rng)
Loop While Not Rng Is Nothing And Rng.Address <> FirstAddress
End If
Next I
End With
With Application
.ScreenUpdating = True
.EnableEvents = True
End With
End Sub
Change this line:
Rng.Copy NewSh.Range("A" & Rcount)
Into this:
Rng.EntireRow.Copy NewSh.Rows(Rcount)
This will copy the whole row of Rng into the whole row of index Rcount, as opposed to just doing a single cell of each.

Range.Find type mismatch error

Here's my code:
Need my code to enter TRUE in a cell if it finds a value in column A of sheet "Itemschedule" to be present in column B of sheet "Whereused". Getting "Type mismatch" error. If I change the "st = Sheets(..." line to .Value instead of .Text, or if I change the .Find line to LookIn:=xlFormulas instead of Lookin:=xlValues, it gives the same error irrespective of the combination of the two.
Private Sub CommandButton1_Click()
Dim rowLast As Integer
Dim str As String
Dim cell As Range
Sheets("Itemschedule").ListObjects("Table2").Range.AutoFilter
rowLast = Sheets("Itemschedule").Cells(Rows.Count, "A").End(xlUp).Row
Sheets("Itemschedule").ListObjects("Table2").Resize Range("A1:E" & rowLast)
Sheets("Itemschedule").Range("A" & rowLast + 1 & ":E" & Rows.Count).ClearContents
For i = 2 To rowLast
str = Sheets("Itemschedule").Cells(i, "A").Text
With Sheets("Whereused").Range("B:B")
Set cell = .Find(What:=str, After:=.Range("B1"), LookIn:=xlValues, LookAt:=xlWhole, SearchOrder:=xlByRows, SearchDirection:=xlNext, MatchCase:=False, SearchFormat:=False)
End With
If cell Is Nothing Then
Sheets("Itemschedule").Cells(i, "E").Value = "FALSE"
Else
Sheets("Itemschedule").Cells(i, "E").Value = "TRUE"
End If
Next
On Error Resume Next
Sheets("Itemschedule").ListObjects("Table2").Range.AutoFilter
Sheets("Itemschedule").Range("A1:E" & rowLast).AutoFilter Field:=1, Criteria1:="FALSE"
Sheets("Itemschedule").Range("A1:E" & rowLast).SpecialCells(xlCellTypeVisible).EntireRow.Delete
Sheets("Itemschedule").ListObjects("Table2").Resize Range("A1:E" & rowLast)
Sheets("Itemschedule").ListObjects("Table2").Range.AutoFilter
End Sub
I have tried a lot but am not able to figure it out. I'm sure it's something silly.
Please help.
Your error is due to the fact that the After parameter is not inside the range you are searching. This part:
With Sheets("Whereused").Range("B:B")
Set cell = .Find(What:=str, After:=.Range("B1"), LookIn:=xlValues, LookAt:=xlWhole, SearchOrder:=xlByRows, SearchDirection:=xlNext, MatchCase:=False, SearchFormat:=False)
End With
should be:
With Sheets("Whereused").Range("B:B")
Set cell = .Find(What:=str, After:=Sheets("Whereused").Range("B1"), LookIn:=xlValues, LookAt:=xlWhole, SearchOrder:=xlByRows, SearchDirection:=xlNext, MatchCase:=False, SearchFormat:=False)
End With
otherwise the .Range("B1") is relative to Range("B:B") and therefore refers to C1.
Dictionaries are more efficient at finding duplicate values.
Sub CommandButton1_Click()
Dim keyword As String, keyvalue As Variant
Dim rowLast As Long, x As Long
Dim dicItems
Set dicItems = CreateObject("scripting.dictionary")
With Sheets("Whereused")
rowLast = .Cells(Rows.Count, "A").End(xlUp).Row
For x = 2 To rowLast
keyword = .Cells(x, 2)
keyvalue = .Cells(x, 2)
'Add Key Value pairs to Dictionary
If Not dicItems.Exists(keyword) Then dicItems.Add keyword, keyvalue
Next
End With
With Sheets("Itemschedule")
rowLast = .Cells(Rows.Count, "A").End(xlUp).Row
For x = 2 To rowLast
keyword = .Cells(x, 1)
.Cells(x, 2) = dicItems.Exists(keyword)
Next
End With
End Sub
But the proper way to do it place a WorkSheet Formula in Itemschedule Column B.
=COUNTIF(Table1[[#This Row],[Items]],Table2[[#Headers],[Items]])>0

Delete rows with based on cell value

I'm trying to search Column A in Sheet2 for the value of A1 in Sheet1.
If it exists, I'd like to delete the whole row in Sheet2.
If it doesn't exist, I'd like the message box to open.
Here's what I have, but I'm struggling with actually deleting the row:
Sub Delete_Rows()
Dim FindString As String
Dim Rng As Range
FindString = Sheets("Sheet1").Range("A1")
If Trim(FindString) <> "" Then
With Sheets("Sheet2").Range("A:A")
Set Rng = .Find(What:=FindString, _
After:=.Cells(.Cells.Count), _
LookIn:=xlValues, _
LookAt:=xlWhole, _
SearchOrder:=xlByRows, _
SearchDirection:=xlNext, _
MatchCase:=False)
If Not Rng Is Nothing Then
'I can't figure out how to delete the row
Else
MsgBox "Not Found"
End If
End With
End If
End Sub
Here is an example based on THIS
You don't need to loop. You can use .Autofilter which is faster than looping.
Sub Sample()
Dim ws1 As Worksheet, ws2 As Worksheet
Dim delRange As Range
Dim lRow As Long
Dim strSearch As String
Set ws1 = Sheet1: Set ws2 = Sheet2
strSearch = ws1.Range("A1").Value
With ws2
'~~> Remove any filters
.AutoFilterMode = False
lRow = .Range("A" & .Rows.Count).End(xlUp).Row
With .Range("A1:A" & lRow)
.AutoFilter Field:=1, Criteria1:="=" & strSearch
Set delRange = .Offset(1, 0).SpecialCells(xlCellTypeVisible).EntireRow
End With
'~~> Remove any filters
.AutoFilterMode = False
End With
If delRange Is Nothing Then
MsgBox "Not Found"
Else
delRange.Delete
End If
End Sub
Here is the code to :
loop through all the values in Column A of Sheet1,
look for all matches (with FindNext method) in Column A of Sheet 2
and delete the rows that matches
Give it a try :
Sub test_user5472539()
Dim Ws1 As Worksheet, _
Ws2 As Worksheet, _
LastRow As Long, _
FindString As String, _
FirstAddress As String, _
cF As Range
Set Ws1 = ActiveWorkbook.Sheets("Sheet1")
Set Ws2 = ActiveWorkbook.Sheets("Sheet2")
LastRow = Ws1.Range("A" & Ws1.Rows.Count).End(xlUp).Row
For i = 1 To LastRow
FindString = Ws1.Range("A" & i)
If Trim(FindString) <> "" Then
Ws2.Range("A1").Activate
With Ws2.Range("A:A")
'First, define properly the Find method
Set cF = .Find(What:=FindString, _
After:=.Cells(1, 1), _
LookIn:=xlValues, _
LookAt:=xlWhole, _
SearchOrder:=xlByRows, _
SearchDirection:=xlNext, _
MatchCase:=False, _
SearchFormat:=False)
'If there is a result, keep looking with FindNext method
If Not cF Is Nothing Then
FirstAddress = cF.Address
Do
cF.EntireRow.Delete
Set cF = .FindNext(cF)
'Look until you find again the first result
Loop While Not cF Is Nothing And cF.Address <> FirstAddress
Else
MsgBox "Not Found"
End If
End With
Else
End If
Next i
End Sub

Creating an Excel Macro to Delete multiple rows at once

I found a code online and want to make edits to it. The code is in VBA and I want the macro code to delete multiple rows rather than one. Here is the code:
Sub findDelete()
Dim c As String
Dim Rng As Range
c = InputBox("FIND WHAT?")
Set Rng = Nothing
Set Rng = Range("A:A").Find(what:=c, _
After:=Range("A1"), _
LookIn:=xlFormulas, _
lookat:=xlPart, _
SearchOrder:=xlByRows, _
SearchDirection:=xlNext, _
MatchCase:=False)
Rng.EntireRow.Delete shift:=xlUp
End Sub
Instead of using find, use Autofilter and delete the VisibleCells
Sub findDelete()
Dim c As String, Rng As Range, wks as Worksheet
c = InputBox("FIND WHAT?")
Set wks = Sheets(1) '-> change to suit your needs
Set Rng = wks.Range("A:A").Find(c, After:=Range("A1"), LookIn:=xlFormulas, _
lookat:=xlPart, SearchOrder:=xlByRows, SearchDirection:=xlNext, _
MatchCase:=False)
If Not Rng Is Nothing Then
With wks
.Range(.Range("A1"), .Range("A" & .Rows.Count).End(xlUp)).AutoFilter 1, c
Set Rng = Intersect(.UsedRange, .UsedRange.Offset(1), .Range("A:A")).SpecialCells(xlCellTypeVisible)
Rng.Offset(1).EntireRow.Delete
End With
End If
End Sub
EDIT
To replace the InputBox with Multiple Values to Find / Delete Do This:
Option Explicit
Sub FindAndDeleteValues()
Dim strValues() as String
strValues() = Split("these,are,my,values",",")
Dim i as Integer
For i = LBound(strValues()) to UBound(strValues())
Dim c As String, Rng As Range, wks as Worksheet
c = strValues(i)
'.... then continue with code as above ...
Next
End Sub
Just wrap it up in a While loop.
Sub findDelete()
Dim c As String
Dim Rng As Range
c = InputBox("FIND WHAT?")
Set Rng = Nothing
Do While Not Range("A:A").Find(what:=c) Is Nothing
Set Rng = Range("A:A").Find(what:=c, _
After:=Range("A1"), _
LookIn:=xlFormulas, _
lookat:=xlPart, _
SearchOrder:=xlByRows, _
SearchDirection:=xlNext, _
MatchCase:=False)
Rng.EntireRow.Delete shift:=xlUp
Loop
End Sub
You already have the code to delete rows in Rng.EntireRow.Delete shift:=xlUp, what you need is the code to set the range to the rows which you want to delete. As usual in VBA, this can be done in a lot of ways:
'***** By using the Rng object
Set Rng = Rows("3:5")
Rng.EntireRow.Delete shift:=xlUp
Set Rng = Nothing
'***** Directly
Rows("3:5").EntireRow.Delete shift:=xlUp
Your Find statement only finds the first occurrence of c, that's why it's not deleting more that one row.