Cleaner way of "finding" a range of values - excel

So I currently a macro that assigns a cell value to a variable and then search for this variable on a another sheet. The problem is that I am having to do this a large number values so I currently have the same code copied 20 times allowing for 20 values to be search in series. Is there a cleaner method of running a repeatable operation like this? Also is it possible to set the upper limit based on the number of values entered. E.g. my current setup looks cells M8:M27 for it's variables, is it possible however to write it so that it is repeated continuously until it hits a blank cell? Thereby letting the user enter as many values as required?
Here is an extract for a single variable. This is then repeated up to reverse_id_20
Sheets("GR Input").Select
reverse_id_1 = Range("O8")
Sheets("PchOrds").Select
Columns("A:A").Select
Selection.Find(What:=reverse_id_1, After:=ActiveCell, LookIn:= _
xlFormulas, LookAt:=xlPart, SearchOrder:=xlByRows, SearchDirection:= _
xlNext, MatchCase:=False, SearchFormat:=False).Select
Rows(ActiveCell.Row).Select
Selection.Delete Shift:=xlUp
Sheets("GR Input").Select
Thanks in advance guys,
Dan

Is there a cleaner method of running a repeatable operation like this?
Yes, it is. You can use loop for it:
Sub test()
Dim reverse_id As Variant
Dim rng As Range
Dim r_id As Variant
With Sheets("GR Input")
reverse_id = .Range("O8:O11")
End With
For Each r_id In reverse_id
Set rng = Sheets("PchOrds").Columns("A:A").Find( _
What:=r_id, _
LookIn:=xlFormulas, _
LookAt:=xlPart, _
SearchOrder:=xlByRows, _
MatchCase:=False)
If Not rng Is Nothing Then
rng.EntireRow.Delete Shift:=xlUp
End If
Next r_id
End Sub
Btw, code above deletes only first row that meet criteria. If you'd like to delete all values from sheet "PchOrds", that meet criteria, use this code:
Sub test1()
Dim reverse_id As Variant
Dim rng As Range
Dim lastrow As Long
Dim r_id As Variant
With Sheets("GR Input")
reverse_id = .Range("O8:O11")
End With
For Each r_id In reverse_id
If r_id <> "" Then
With Sheets("PchOrds")
lastrow = Application.Max(2, .Cells(.Rows.Count, "A").End(xlUp).Row)
.AutoFilterMode = False
With .Range("A1:A" & lastrow)
.AutoFilter Field:=1, Criteria1:="=*" & r_id & "*"
.Offset(1, 0).Resize(lastrow - 1, 1).EntireRow.Delete
End With
.AutoFilterMode = False
End With
End If
Next r_id
End Sub

Related

Selecting a range until the last used row

I am trying to select a range until the last used row in the sheet. I currently have the following:
Sub Select_Active_Down()
Dim lr As Long
lr = ActiveSheet.UsedRange.Rows.Count
If Cells(ActiveCell.Row, ActiveCell.Column) = Cells(lr, ActiveCell.Column) Then
MsgBox "There isn't any data to select."
Else
Range(Cells(ActiveCell.Row, ActiveCell.Column), Cells(lr, ActiveCell.Column)).Select
Cells(lr, ActiveCell.Column).Activate
End If
End Sub
The issue is that I need to select multiple columns, and this will only select the first column of the active range. How can I modify this to select multiple columns rather than just the first?
What about selection the entire region? This can be done as follows in VBA:
Selection.CurrentRegion.Select
There also is the possibility to select the entire array. For that, just press Ctrl+G, choose Special and see over there.
I would do this slightly different. I would use .Find to find the last row and the last column (using the same logic shown in the link) to construct my range rather than using Selection | Select | ActiveCell | UsedRange | ActiveSheet.
Is this what you are trying?
Option Explicit
Sub Sample()
Dim ws As Worksheet
Dim LastRow As Long
Dim LastColumn As Long
Dim rng As Range
'~~> Change it to the relevant sheet
Set ws = Sheet1
With ws
'~~> Check if there is data
If Application.WorksheetFunction.CountA(.Cells) = 0 Then
MsgBox "No Data Found"
Exit Sub
End If
'~~> Find last row
LastRow = .Cells.Find(What:="*", _
After:=.Range("A1"), _
Lookat:=xlPart, _
LookIn:=xlFormulas, _
SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious, _
MatchCase:=False).Row
'~~> Find last column
LastColumn = .Cells.Find(What:="*", _
After:=.Range("A1"), _
Lookat:=xlPart, _
LookIn:=xlFormulas, _
SearchOrder:=xlByColumns, _
SearchDirection:=xlPrevious, _
MatchCase:=False).Column
'~~> Construct your range
Set rng = .Range(.Cells(1, 1), .Cells(LastRow, LastColumn))
'~~> Work with the range
With rng
MsgBox .Address
'
'~~> Do what you want with the range here
'
End With
End With
End Sub

Copy data between work books with dynamic cells

Trying to copy data from one Excel spreadsheet to another (from New_data to report).
In the New_data spreadsheet I find the second time System (hence why I start the search below the first one at N21) appears then I need to copy all data below it from columns b - k until I hit blank cells. How do I get the amount of rows to only capture filled cells?
Range("B584:K641") needs to be dynamic.
Sub CopyWorkbook()
Range("N21").Select
Cells.Find(What:="system", After:=ActiveCell, LookIn:=xlFormulas, LookAt _
:=xlPart, SearchOrder:=xlByRows, SearchDirection:=xlNext, MatchCase:= _
False, SearchFormat:=False).Activate
Range("B584:K641").Select
Selection.Copy
Application.WindowState = xlNormal
Windows("report.xlsx").Activate
Range("A2").Select
ActiveSheet.Paste
Windows("new_data.csv"). _
Activate
End Sub
Try the next code please. It should be very fast (if I correctly understood where to be searched for 'system', starting with what...). The code assumes that "new_data.csv" is the csv workbook name. If not, you must use its real name when defining shCSV sheet:
Sub CopyWorkbook()
Dim shR As Worksheet, shCSV As Worksheet, lastRow As Long, systCell As Range, arr
Set shR = Workbooks("report.xlsx").ActiveSheet 'use here the sheet you need to paste
'it should be better to use the sheet name.
'No need to have the respective sheet activated at the beginning
Set shCSV = Workbooks("new_data.csv").Sheets(1) 'csv file has a single sheet, anyhow
lastRow = shCSV.Range("B" & rows.count).End(xlUp).row
Set systCell = shCSV.Range("B21:B" & lastRow).Find(What:="system", _
After:=shCSV.Range("B21"), LookIn:=xlFormulas, LookAt _
:=xlPart, SearchOrder:=xlByRows, SearchDirection:=xlNext, MatchCase:= _
False, SearchFormat:=False)
If systCell Is Nothing Then MsgBox "No 'sytem' cell has been found...": Exit Sub
arr = shCSV.Range(systCell, shCSV.Range("K" & lastRow)).Value
shR.Range("A2").Resize(UBound(arr), UBound(arr, 2)).Value = arr
End Sub
Try:
Sub test()
Dim LR As Long
Dim Ini As Long
LR = Range("B" & Rows.Count).End(xlUp).Row 'last non empty row in column B
Ini = Application.WorksheetFunction.Match("system", Range("N21:N" & LR), 0) + 20 'position of system after n21
Range("B" & Ini & ":K" & LR).Copy
'''rest of your code to paste
End Sub
Note that this code is searching word system only in column N. If it's somewhere else, you'll need to adapt the MATCH function
I set a range to equal the filtered range and start a loop to count how many none empty cells occur until the first empty cell in column B.
Sub CopyWorkbook()
ThisWorkbook.Sheets("new_data").Activate
Range("N21").Select
Dim rng As Range
Set rng = Cells.Find(What:="system", After:=ActiveCell, _
LookIn:=xlFormulas, _
LookAt:=xlPart, SearchOrder:=xlByRows, SearchDirection:=xlNext, _
MatchCase:=False, _
SearchFormat:=False)
Dim i As Double
i = rng.Row
Do Until ThisWorkbook.Sheets("new_data").Range("B" & i) = vbNullString
i = i + 1
Loop
i = i - 1
Range("B" & rng.Row & ":K" & i).Select
Selection.Copy
Application.WindowState = xlNormal
Windows("report.xlsx").Activate
Range("A2").Select
ActiveSheet.Paste
Windows("new_data.csv").Activate
End Sub
I found a Stack Overflow question that was helpful in finding an answer. Find cell address

Find a value in range + loop

I need my macro to Look at a cell in my range, Find that value in the a different WS and paste a value on to that's next to the value i'm looking for (my original WS). do this again and again to the values in the range.
now it all works but for some reason the value is stuck on the first search and wont look for other values in the original range.
here is my code, and the pictures should help.
Sub Macro1()
'
'now im gonna match the "UDD" TO THE "S/O"
Worksheets("Sheet1").Activate
Range("c17").Select
Dim Searchkey As Range, cell As Range
Set Searchkey = Range("c17:c160")
For Each cell In Searchkey
Sheets("data").Activate
Cells.Find(What:=Searchkey, After:=ActiveCell, LookIn:=xlFormulas, LookAt _
:=xlPart, SearchOrder:=xlByRows, SearchDirection:=xlNext, MatchCase:= _
False, SearchFormat:=False).Activate
ActiveCell.Offset(0, -1).Range("A1").Select
Application.CutCopyMode = False
Selection.Copy
Sheets("Sheet1").Activate
ActiveCell.Offset(0, 1).Range("A1").Select
ActiveSheet.Paste
ActiveCell.Offset(1, -1).Range("A1").Select
Selection.Copy
Next cell
End Sub
why is my macro stuck on "84225" and not looping to the other S/O?
Thank you
Sub mac1()
Worksheets("Sheet1").Activate
Range("c17").Select
Dim srch As Range, cell As Variant
Set srch = Range("c17:c160")
For Each cell In srch
Sheets("data").Activate
Cells.Find(What:=cell, LookIn:=xlValues, LookAt:= _
xlPart, SearchOrder:=xlByRows, SearchDirection:=xlNext, MatchCase:=False _
, SearchFormat:=False).Activate
ActiveCell.Offset(0, -1).Range("A1").Select
Application.CutCopyMode = False
Selection.Copy
Sheets("Sheet1").Activate
ActiveCell.Offset(0, 1).Range("A1").Select
ActiveSheet.Paste
ActiveCell.Offset(1, -1).Range("A1").Select
Selection.Copy
Next cell
End Sub
this is working!
thank you all
On each loop you're searching for the whole range of SearchKey and not just Cell so I'm guessing it's always using the first cell in SearchKey as your search criteria.
You're also searching in the formula rather than the values, and looking for a part match which may return incorrect results (part match on 20 would return a find in 20, 201, 11120001, etc).
Not qualifying your sheet names and using Activate probably isn't helping much either.
Try this code:
Public Sub Test()
Dim SrcSht As Worksheet, TgtSht As Worksheet
Dim SearchKey As Range, Cell As Range
Dim FoundValue As Range
With ThisWorkbook
Set SrcSht = .Worksheets("Sheet1")
Set TgtSht = .Worksheets("Data")
End With
Set SearchKey = SrcSht.Range("C17:C21")
For Each Cell In SearchKey
'Search column 3 (C) for your the value
Set FoundValue = TgtSht.Columns(3).Find(What:=Cell, _
After:=TgtSht.Columns(3).Cells(1, 1), _
LookIn:=xlValues, _
LookAt:=xlWhole, _
SearchOrder:=xlByRows, _
SearchDirection:=xlNext)
'Only proceed if value found, otherwise an error will occur.
If Not FoundValue Is Nothing Then
Cell.Offset(, 1) = FoundValue.Offset(, 1)
End If
Next Cell
End Sub
Edit:
To test the code place the cursor within the procedure and press F8 to process each line in turn. The FoundValue should contain a value each time it's executed.
To check this hover your cursor over the variable to see its value:
The row highlighted in yellow is the next line that will be executed. If FoundValue is nothing then the following line isn't processed, if it's not nothing then the line is executed.

Change Loop to Auto Filter

I want to simplify the following code by changing the loop structure to an auto filter structure.
1
ActiveCell.Columns("A:A").EntireColumn.Select
If Selection.Find(What:="~* C", After:=ActiveCell, LookIn:=xlValues, _
LookAt:=xlPart, SearchOrder:=xlByColumns, SearchDirection:=xlNext, _
MatchCase:=True) Is Nothing Then
GoTo 2
End If
Selection.Find(What:="~* C", After:=ActiveCell, LookIn:=xlValues, _
LookAt:=xlPart, SearchOrder:=xlByColumns, SearchDirection:=xlNext, _
MatchCase:=True).Activate
ActiveCell.Select
Range(Selection, Selection.Offset(0, 1)).Insert shift:=xlToRight
GoTo 1
2
Try this one:
Sub test()
Dim lastrow As Long
Dim rng As Range
Dim ar As Range
'change Sheet1 to suit
With ThisWorkbook.Worksheets("Sheet1")
lastrow = .Cells(.Rows.Count, "A").End(xlUp).Row 'find last row in column A
.AutoFilterMode = False 'remove previous filter
With .Range("A1:A" & lastrow)
.AutoFilter Field:=1, Criteria1:="*~* C*" 'apply filter
On Error Resume Next
Set rng = .Offset(1).Resize(.Rows.Count - 1).SpecialCells(xlCellTypeVisible) 'find visible rows
On Error GoTo 0
End With
.AutoFilterMode = False 'remove filter
'if we found some values - insert
If Not rng Is Nothing Then
rng.Insert Shift:=xlToRight
rng.Insert Shift:=xlToRight
End If
End With
End Sub
If your column A doesn't contain header, use this one for rng:
Set rng = .SpecialCells(xlCellTypeVisible)
Btw, this post may help you in future: How to avoid using Select/Active statements
ActiveCell.Columns("A:A").EntireColumn.Select
Selection.AutoFilter 'resets any current autofilter
Selection.AutoFilter Field:=1, Criteria1:="=~* C", Operator:=xlFilterValues
and once filter is applied I usually use something like:
dim rng as range
set rng = ActiveSheet.cells.SpecialCells(xlCellTypeVisible)
that gets you all of the visible cells, which with a filter active, are only the ones that match the filter criteria.
edit
at the beginning do this:
dim numrows as long
dim numcolumns as long
numrows = Cells.find("*", [A1], , , xlByRows, xlPrevious).Row
numcolumns = Cells.find("*", [A1], , , xlByColumns, xlPrevious).Column
then before filtering do this: set rng = Range("A1", Cells(numrows,numcolumns))
and then after filter, instead of Activesheet use: set rng = rng.cells.SpecialCells(xlCellTypeVisible) so that way it gets only the visible cells within the used range

Need a find function to search one column of one sheet

I need a find function to search in Column B of my worksheet titled "Quote Sheet" to find "Profit Adjustment" and it would be nice if it was case sensitive. Below is the code I am working with but I can't get the range or the wording correct. Any help is appreciated.
Dim rFound As Range
Set rFound = Range("B10:B1000")
Cells.Find(What:="Profit Adjustment", After:=ActiveCell, LookIn:=xlFormulas, LookAt:=xlPart, SearchOrder:=xlByRows, SearchDirection:=xlNext, MatchCase:=True).Activate
ActiveCell.Offset(0, 8).Select
Selection.Copy
Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=False
Sub samPle()
Dim rFound As Range, cellToFind As Range
Set rFound = Sheets("Quote Sheet").Range("B10:B1000")
Set cellToFind = Cells.Find(What:="Profit Adjustment", MatchCase:=True)
If Not cellToFind Is Nothing Then
cellToFind.Activate
ActiveCell.Offset(0, 8).Copy ActiveCell.Offset(0, 8)
End If
End Sub
I would re-write your example like this:
Sub copy_paste_example()
Dim c As Range
With ActiveWorkbook.Sheets("Quote Sheet")
Set c = .Columns("B").Find(What:="Profit Adjustment", _
LookIn:=xlFormulas, LookAt:=xlPart, _
SearchOrder:=xlByRows, _
SearchDirection:=xlNext, MatchCase:=True)
On Error GoTo NotFoundErr:
c.Offset(0, 8).Value = c.Value
End With
Exit Sub
NotFoundErr:
Debug.Print "value not found"
End Sub
Notes:
You weren't ever using the rfound Range Object so I removed it.
Activate and Select are not needed, and may even slow down your code.
Remember that Find returns a Range Object which can be useful later on in your code.
It wasn't immediately clear to me whether you only want to find the first occurrence of Profit Adjustment, or if you care about all occurrences. If you want to find all rows in Column B that contain Profit Adjustment, the below Macro will work as-is. If you want to find only the first occurrence, then simply uncomment the line that says Exit For.
Here's the code:
Sub FindValueInColumn()
Dim rowCount As Integer, currentRow As Integer, columnToSearch As Integer
Dim searchString As String
Dim quoteSheet As Worksheet
Set quoteSheet = ActiveWorkbook.Sheets("Quote Sheet")
searchString = "Profit Adjustment" 'string to look for
columnToSearch = 2 '2 represents column B
rowCount = quoteSheet.Cells(Rows.Count, columnToSearch).End(xlUp).Row
For currentRow = 1 To rowCount
If Not Cells(currentRow, columnToSearch).Find(What:=searchString, MatchCase:=True) Is Nothing Then
Cells(currentRow, 8).Value = Cells(currentRow, columnToSearch).Value
'uncomment the below "Exit For" line if you only care about the first occurrence
'Exit For
End If
Next
End Sub
Before search:
After search:

Resources