I am using VBA to search a 15x15 array for the location (row and column number) of all the '1's and map them to a new location.
Sub findvalues()
Dim OldRow As Long
Dim OldColumn As Long
With Worksheets(1).Range("a1:o15")
Set c = .Find(1, LookIn:=xlValues)
If Not c Is Nothing Then
firstAddress = c.Address
Do
cellAddress = c.Address
OldRow = Range(cellAddress).Row
OldCol = Range(cellAddress).Column
MsgBox (OldRow)
With Worksheets(1).Range("r3:r16")
Set oldmapping = .Find(OldRow, LookIn:=xlValues, LookAt:=xlWhole)
NewCol = oldmapping.Offset(, 1).Value
MsgBox (NewCol)
End With
Set c = .FindNext(c)
MsgBox (c.Address)
Loop While Not c Is Nothing And c.Address <> firstAddress
End If
End With
End Sub
The strange thing is, the code runs fine until the fourth loop where it gets to MsgBox (c.Address) and throws a
runtime error 91 - object variable not set
I have a feeling it is down to the Set c = .FindNext(c) on the line previous but can't figure out why as it works fine for the first 3 loops.
The .FindNext derived from,
Set c = .Find(1, LookIn:=xlValues)
... is being superseded by the intermediate,
Set oldmapping = .Find(OldRow, LookIn:=xlValues, LookAt:=xlWhole)
Your .FindNext and loop conditions are no longer valid.
Switch to an alternate method for the second .Find.
Option Explicit
Sub findvalues()
Dim OldRow As Long, OldCol As Long, NewCol As Long
Dim oldmapping As Variant, c As Range, firstAddress As String, cellAddress As String
With Worksheets(1).Range("a1:o15")
Set c = .Find(1, LookIn:=xlValues)
If Not c Is Nothing Then
firstAddress = c.Address
Do
cellAddress = c.Address
OldRow = Range(cellAddress).Row
OldCol = Range(cellAddress).Column
MsgBox (OldRow)
oldmapping = Application.Match(OldRow, Worksheets(1).Range("r3:r16"), 0)
If Not IsError(oldmapping) Then
NewCol = Worksheets(1).Range("r3:r16").Cells(oldmapping).Offset(, 1).Value
MsgBox NewCol
End If
Set c = .FindNext(c)
MsgBox (c.Address)
Loop While Not c Is Nothing And c.Address <> firstAddress
End If
End With
End Sub
A loop while (or do while) will still run once when the test is true, here is a simple example:
Sub temp()
Dim X As Long
X = 0
Do
X = X + 1
Debug.Print X
Loop While X < 5
End Sub
Notice 5 shows up in the list of values in your debug window.
A simple test should fix this up for you.
If Not c Is Nothing then MsgBox (c.Address)
or you could tell it to exit the loop if c is nothing, your choice
Related
Following is the issue:
1) I force some Items to be selected in a ListBox over VBA Code
2) Open UserForm and select or deselect some Items. (everything seems fine in the ListBox)
3) Write the selected Items out.
If i only select some new items, everyhing works fine. If i deselect a selected item, which i forced at the beginning to be selected, it is still selected in the output.
With Sheets("ID_Mitarbeiter").Range("A2:A1048576")
Set c = .Find(What:=TextBox_ID, lookat:=xlWhole)
If Not c Is Nothing Then
firstAddress = c.Address
Do
ListBox_Mitarbeiter.Selected(Sheets("ID_Mitarbeiter").Cells(c.Row, 2) - 1) = True
Set c = .FindNext(c)
If c Is Nothing Then
GoTo DoneFinding
End If
Loop While c.Address <> firstAddress
End If
DoneFinding:
End With
Private Sub Butto_Change_Click()
Dim ind_pers As Integer
With ListBox_Mitarbeiter
If .ListCount > 0 Then
ind_pers = 0
For i = 0 To .ListCount - 1
If .Selected(i) = True Then
Cells(ind_pers, 1).Value = 1
Cells(ind_pers, 2).Value = i + 1
ind_pers = ind_pers + 1
Else
End If
Next i
Else
End If
End With
End Sub
Private Sub ComboBox1_Change()
TextBox_IndexStart.Value = Sheets("ID_Mitarbeiter").Range("A2:A1048576").Find(What:=TextBox_ID.Value, lookat:=xlWhole).Row
With Sheets("ID_Mitarbeiter").Range("A2:A1048576")
Set c = .Find(What:=TextBox_ID, lookat:=xlWhole)
If Not c Is Nothing Then
firstAddress = c.Address
Do
ListBox_Mitarbeiter.Selected(Sheets("ID_Mitarbeiter").Cells(c.Row, 2) - 1) = True
Set c = .FindNext(c)
If c Is Nothing Then
GoTo DoneFinding
End If
Loop While c.Address <> firstAddress
End If
DoneFinding:
End With
End If
End Sub
Private Sub UserForm_Initialize()
Dim last As Integer
Dim cnt As Integer
last = Sheets("Mitarbeiter").Cells(Rows.Count, 1).End(xlUp).Row
For cnt = 2 To last
With ListBox_Mitarbeiter
.AddItem Sheets("Mitarbeiter").Cells(cnt, 2).Value & " " & Sheets("Mitarbeiter").Cells(cnt, 3).Value
End With
Next cnt
End Sub
This code works fine. But when I, in the User Interface, deselect the items, it still writes out all the selected items in the first place.
This is an addition to my question yesterday so I am starting a new question. Basically I get different ranges of data on a sheet in excel and data range vary each week so last used column and last used row vary.
I would like to merge row 3 and 4 based on names and I will post a sample data so you can understand what I am trying to achieve. Row 3 is the one that has the names and row 4 is always empty. Right now, I am getting error 91, Object variable or With block variable not set on Loop While that line.
And again, I am only showing you 3 ranges since it is best fit on the picture.
Sub test()
'Set Up
Dim f, g, h, i, j, k As Range
Dim firstaddress As String
Dim ws1 As Worksheet
Set ws1 = Sheets("Sheet1")
'Merge back
With ws1.Rows(3)
Set f = .Find("A", LookIn:=xlValues)
If Not f Is Nothing Then
firstaddress = f.Address
Do
Range(f.Resize(2), f.Resize(, 1)).Merge
Range(f.Resize(2), f.End(xlToRight)).BorderAround LineStyle:=xlContinuous, Weight:=xlMedium
Set f = .FindNext(f)
Loop While Not f Is Nothing And f.Address <> firstaddress
End If
End With
With ws1.Rows(3)
Set g = .Find("B", LookIn:=xlValues)
If Not g Is Nothing Then
firstaddress = g.Address
Do
Range(g.Resize(2), g.Resize(, 1)).Merge
Range(g.Resize(2), g.End(xlToRight)).BorderAround LineStyle:=xlContinuous, Weight:=xlMedium
Set g = .FindNext(g)
Loop While Not g Is Nothing And g.Address <> firstaddress
End If
End With
With ws1.Rows(3)
Set h = .Find("C", LookIn:=xlValues)
If Not h Is Nothing Then
firstaddress = h.Address
Do
Range(h.Resize(2), h.Resize(, 1)).Merge
Range(h.Resize(2), h.End(xlToRight)).BorderAround LineStyle:=xlContinuous, Weight:=xlMedium
Set g = .FindNext(h)
Loop While Not h Is Nothing And h.Address <> firstaddress
End If
End With
With ws1.Rows(3)
Set i = .Find("D", LookIn:=xlValues)
If Not i Is Nothing Then
firstaddress = i.Address
Do
Range(i.Resize(2), i.Resize(, 1)).Merge
Set i = .FindNext(i)
Loop While Not i Is Nothing And i.Address <> firstaddress
End If
End With
With ws1.Rows(3)
Set j = .Find("E", LookIn:=xlValues)
If Not j Is Nothing Then
firstaddress = j.Address
Do
Range(j.Resize(2), j.Resize(, 1)).Merge
Range(j.Resize(2), j.End(xlToRight)).BorderAround LineStyle:=xlContinuous, Weight:=xlMedium
Set j = .FindNext(j)
Loop While Not j Is Nothing And j.Address <> firstaddress
End If
End With
With ws1.Rows(3)
Set k = .Find("F", LookIn:=xlValues)
If Not k Is Nothing Then
firstaddress = k.Address
Do
Range(k.Resize(2), k.Resize(, 1)).Merge
Set k = .FindNext(k)
Loop While Not k Is Nothing And k.Address <> firstaddress
End If
End With
End Sub
Can you try this. I think you can shorten your code with a loop. The error I think is caused by the merging of cells which screws up the Find. Merged cells are a bad idea for many reasons.
Sub test()
'Set Up
Dim f As Range
Dim firstaddress As String
Dim ws1 As Worksheet
Dim v, i As Long
Set ws1 = Sheets("Sheet1")
v = Array("A", "B", "C", "D")
'Merge back
For i = LBound(v) To UBound(v)
With ws1.Rows(3)
Set f = .Find(v(i), LookIn:=xlValues)
If Not f Is Nothing Then
firstaddress = f.Address
Do
f.Resize(2).Merge
Range(f.Resize(2), f.End(xlToRight)).BorderAround LineStyle:=xlContinuous, Weight:=xlMedium
Set f = .FindNext(f)
Loop While Not f Is Nothing
End If
End With
Next i
End Sub
A loop from ASCII character 65 (e.g. A) through ASCII character 90 (e.g. Z) should clean up your code.
Option Explicit
Sub Macro1()
Dim c As Long, firstaddress As String, f As Range, ffs As Range
With Worksheets("sheet1").Rows(3).Cells
.Resize(2, .Columns.Count).UnMerge
Set f = Nothing
For c = 65 To 90
Set f = .Find(Chr(c), LookIn:=xlValues, Lookat:=xlWhole)
If Not f Is Nothing Then
Set ffs = f
firstaddress = f.Address
Do
Set ffs = Union(f, ffs)
Set f = .FindNext(after:=f)
Loop While f.Address <> firstaddress
With Union(ffs, ffs.Offset(1))
.Merge
.BorderAround LineStyle:=xlContinuous, Weight:=xlMedium
End With
End If
Next c
End With
End Sub
i'm writing a code and i'm stuck on this problem which i think should not bee too difficult to solve but i don't manage it.
I need my program to find all cells with a particular value and select them. But they should remain selected at the end of the sub.
So i changed a bit a code i found on the web and wrote that:
Sub FindAll()
With Worksheets(4).Range("a1:l500")
Set c = .Find("myValue", LookIn:=xlValues)
If Not c Is Nothing Then
firstAddress = c.Address
Do
Worksheets(4).Range(c.Address).Activate
Set c = .FindNext(c)
Loop While Not c Is Nothing And c.Address <> firstAddress
End If
End With
End Sub
Of course it selects them in sequence but they do not remain selected, so at the end i just have the last found cell selected
Can anyone help me solve that?
Thanks in advance
Use the Union method to collect the ranges into one discontiguous range then .Select them before leaving the sub
Sub FindAll()
Dim firstAddress As String, c As Range, rALL As Range
With Worksheets(4).Range("a1:l500")
Set c = .Find("myValue", LookIn:=xlValues)
If Not c Is Nothing Then
Set rALL = c
firstAddress = c.Address
Do
Set rALL = Union(rALL, c)
Worksheets(4).Range(c.Address).Activate
Set c = .FindNext(c)
Loop While Not c Is Nothing And c.Address <> firstAddress
End If
.Activate
If Not rALL Is Nothing Then rALL.Select
End With
End Sub
As #Jeeped has already answered, using the Union Method will achieve what you were after.
If the range you were searching for values within was to increase, it would be more efficient to use an Array to hold the values; you can then search the array instead of the worksheet.
Just something to think about for the future.
Option Explicit
Sub arrayFindAll()
Dim wb As Workbook, ws As Worksheet
Dim myArr() As Variant, myCells() As Integer
Dim i As Long, j As Integer, k As Integer, m As Integer
Dim valOccurence As Integer
Dim unionCells As Range, lookupRng As Range
Set wb = ThisWorkbook
Set ws = wb.Sheets(4)
Set lookupRng = ws.Range("A1:L500")
myArr = lookupRng
valOccurence = WorksheetFunction.CountIf(lookupRng, "myValue") - 1
ReDim myCells(0 To valOccurence, 0 To 1)
For i = LBound(myArr, 1) To UBound(myArr, 1)
For j = LBound(myArr, 2) To UBound(myArr, 2)
If myArr(i, j) = "myValue" Then
For k = 0 To UBound(myCells, 1)
If myCells(k, 0) = 0 Then
myCells(k, 0) = i
myCells(k, 1) = j
Exit For
End If
Next k
End If
Next j
Next i
Set unionCells = Cells(myCells(m, 0), myCells(m, 1))
For m = 1 To valOccurence
Set unionCells = Union(unionCells, Cells(myCells(m, 0), myCells(m, 1)))
Next m
unionCells.Select
End Sub
i'm writing a code and i'm stuck on this problem which i think should not bee too difficult to solve but i don't manage it.
I need my program to find all cells with a particular value and select them. But they should remain selected at the end of the sub.
So i changed a bit a code i found on the web and wrote that:
Sub FindAll()
With Worksheets(4).Range("a1:l500")
Set c = .Find("myValue", LookIn:=xlValues)
If Not c Is Nothing Then
firstAddress = c.Address
Do
Worksheets(4).Range(c.Address).Activate
Set c = .FindNext(c)
Loop While Not c Is Nothing And c.Address <> firstAddress
End If
End With
End Sub
Of course it selects them in sequence but they do not remain selected, so at the end i just have the last found cell selected
Can anyone help me solve that?
Thanks in advance
Use the Union method to collect the ranges into one discontiguous range then .Select them before leaving the sub
Sub FindAll()
Dim firstAddress As String, c As Range, rALL As Range
With Worksheets(4).Range("a1:l500")
Set c = .Find("myValue", LookIn:=xlValues)
If Not c Is Nothing Then
Set rALL = c
firstAddress = c.Address
Do
Set rALL = Union(rALL, c)
Worksheets(4).Range(c.Address).Activate
Set c = .FindNext(c)
Loop While Not c Is Nothing And c.Address <> firstAddress
End If
.Activate
If Not rALL Is Nothing Then rALL.Select
End With
End Sub
As #Jeeped has already answered, using the Union Method will achieve what you were after.
If the range you were searching for values within was to increase, it would be more efficient to use an Array to hold the values; you can then search the array instead of the worksheet.
Just something to think about for the future.
Option Explicit
Sub arrayFindAll()
Dim wb As Workbook, ws As Worksheet
Dim myArr() As Variant, myCells() As Integer
Dim i As Long, j As Integer, k As Integer, m As Integer
Dim valOccurence As Integer
Dim unionCells As Range, lookupRng As Range
Set wb = ThisWorkbook
Set ws = wb.Sheets(4)
Set lookupRng = ws.Range("A1:L500")
myArr = lookupRng
valOccurence = WorksheetFunction.CountIf(lookupRng, "myValue") - 1
ReDim myCells(0 To valOccurence, 0 To 1)
For i = LBound(myArr, 1) To UBound(myArr, 1)
For j = LBound(myArr, 2) To UBound(myArr, 2)
If myArr(i, j) = "myValue" Then
For k = 0 To UBound(myCells, 1)
If myCells(k, 0) = 0 Then
myCells(k, 0) = i
myCells(k, 1) = j
Exit For
End If
Next k
End If
Next j
Next i
Set unionCells = Cells(myCells(m, 0), myCells(m, 1))
For m = 1 To valOccurence
Set unionCells = Union(unionCells, Cells(myCells(m, 0), myCells(m, 1)))
Next m
unionCells.Select
End Sub
I am trying to perform a type of nested find request, the use case is that I need to look for a group on one worksheet, if found take the user ID value from a seperate column in the found row and then search for that ID in another sheet. It should then perform a bunch of actions and then find the next occurance of group in the first sheet.
The code I have is
LookupGroup = Split("GroupName1,GroupName2", ",")
For I = 0 To UBound(LookupGroup)
With Worksheets("RawData").Range("C:C")
Set C = .Find(LookupGroup(I), LookIn:=xlValues)
If Not C Is Nothing Then
FirstAddress = C.Address
Do
LookupId = Sheets("RawData").Cells(C.Row, 7).Value
IdExist = False
'Check to ensure ID does not exists on Team Members Tab
Set IdRange = Sheets("Team Members").Range("A:A").Find(LookupId, LookIn:=xlValues)
If IdRange Is Nothing Then
IdExist = True
End If
If Not IdExist Then
Highlight = True 'trigger to Set row to bold red font
If RecordsFound > 0 Then
TotalRecords = TotalRecords + RecordsFound
End If
End If
Set C = .FindNext(C)
Loop While Not C Is Nothing And C.Address <> FirstAddress
End If
End With
Next I
This works fine the first time through, however upon reaching the Set C = .FindNext(C) the command returns 'Nothing' rather than the next occurence.
If I comment out the second find
Set IdRange = Sheets("Team Members").Range("A:A").Find(LookupId, LookIn:=xlValues)
Then the first search works fine and finds all instances
What am I doing wrong?
Easier to take the Find() logic and put it in a separate function...
Sub Tester()
Dim LookupGroup, rngGrp As Range, rngMember As Range, I
Dim g As Range, m As Range
LookupGroup = Split("GroupName1,GroupName2", ",")
For I = 0 To UBound(LookupGroup)
Set rngGrp = FindAll(Worksheets("RawData").Range("C:C"), LookupGroup(I))
If Not rngGrp Is Nothing Then
For Each g In rngGrp.Cells
Set rngMember = FindAll(Sheets("Team Members").Range("A:A"), _
g.EntireRow.Cells(7))
If Not rngMember Is Nothing Then
For Each m In rngMember.Cells
'do something with m
Next m
Else
'flag not found...
End If
Next g
End If
Next I
End Sub
'find all matching cells in a given range
Function FindAll(rngLookIn As Range, LookFor) As Range
Dim rv As Range, c As Range, FirstAddress As String
With rngLookIn
Set c = .Find(LookFor, LookIn:=xlValues, lookat:=xlWhole)
If Not c Is Nothing Then
FirstAddress = c.Address
Set rv = c
Do
Set c = .FindNext(c)
If Not c Is Nothing Then Set rv = Application.Union(rv, c)
Loop While Not c Is Nothing And c.Address <> FirstAddress
End If
End With
Set FindAll = rv
End Function
I know it is an old question but it can do what you want replacing FindNext with another search but in a limited range, not the entire column "C".
First find the last row of "C" with the LastRow function and use Find with Worksheets("RawData").Range("C1:C" & LRow).
At the end, instead of using FindNext, use Find again with Range("C" & C.Row + 1 & ":C" & LRow)
Public Function LastRow(ByRef wsSheet_I As Worksheet, ByVal lColumn_I As Long) As Long
Dim LRow As Range
Set LRow = wsSheet_I.Columns(lColumn_I).Find( _
What:="*", _
LookIn:=xlFormulas, _
Lookat:=xlPart, _
SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious, _
MatchCase:=False)
If Not LRow Is Nothing Then
LastRow = LRow.Row
Else
LastRow = 0
End If
End Function
Public Sub FindInFind()
LookupGroup = Split("GroupName1,GroupName2", ",")
For i = 0 To UBound(LookupGroup)
'new code
Dim LRow As Long
LRow = LastRow(Worksheets("RawData"), 3)
If LRow = 0 Then GoTo ErrorHandling
Dim C As Range
Set C = Worksheets("RawData").Range("C1:C" & LRow).Find(LookupGroup(i), LookIn:=xlValues)
'end new code
'With Worksheets("RawData").Range("C:C")
'Set C = .Find(LookupGroup(i), LookIn:=xlValues)
If Not C Is Nothing Then
'FirstAddress = C.Address
Do
LookupId = Sheets("RawData").Cells(C.Row, 7).Value
IdExist = False
'Check to ensure ID does not exists on Team Members Tab
Set IdRange = Sheets("Team Members").Range("A:A").Find(LookupId, LookIn:=xlValues)
If IdRange Is Nothing Then
IdExist = True
End If
If Not IdExist Then
Highlight = True 'trigger to Set row to bold red font
If RecordsFound > 0 Then
TotalRecords = TotalRecords + RecordsFound
End If
End If
'Set C = .FindNext(C)
'new code
Set C = Worksheets("RawData").Range("C" & C.Row + 1 & ":C" & LRow) _
.Find(LookupGroup(i), LookIn:=xlValues)
'end new code
Loop While Not C Is Nothing 'And C.Address <> FirstAddress
End If
End With
Next i
'new code
Exit Sub
ErrorHandling:
'do something with the error
'end new code
End Sub