I get the 'Object Required' error when trying to parse a value from a list into an array. The weird thing is, is that I do it once and it works, but when I try and parse the second value with a different offset it gives me the 'Object Required' error.
Sub Run_Click()
Dim ArrVal() As Variant
Dim DateRange As Range
Dim ComValue() As Variant
Dim LastRow As Long
Dim i As Long
Dim numRow As Variant
Dim sh2 As Worksheet
Dim ConvertVal As String
Dim check As Variant
Dim DutyTest() As Variant
Set sh2 = Sheets(2)
With Sheets("Sheet1")
LastRow = .Range("A" & .Rows.Count).End(xlUp).Row - 7
ReDim Preserve ComValue(1 To LastRow)
ReDim Preserve ArrVal(1 To LastRow)
ReDim Preserve DutyTest(1 To LastRow)
Set DateRange = .Range("A" & 8 & ":A" & 7 + LastRow)
i = 1
Do While i <= LastRow
If i > LastRow Then GoTo ErrorHandler
For Each RowCount In DateRange
'On Error GoTo ErrorHandler
ComValue(i) = .Range("A" & i + 7).Value
ConvertVal = CStr(ComValue(i))
numRow = Application.Match(ConvertVal, sh2.Range("A1:A990000"), 0)
ArrVal(i) = sh2.Range("A" & numRow).Offset(0, 2).Value
DutyTest(i) = sht2.Range("A" & numRow).Offset(1, 1).Value
If i = LastRow Then
Range("B8").Resize(UBound(ArrVal), 1).Formula = Application.Transpose(ArrVal)
End If
i = i + 1
Next RowCount
Loop
ErrorHandler:
End With
End Sub
The error is pulled on the DutyTest(i) line below the ComValue(i) line which gets returned fine. The only difference I can think of is that ComValue(i) is a percent that is returned, in the offset field for DutyTest(i) it should return a string instead, could that be causing the issue?
You have written sht2 as the sheet reference instead of sh2. This is the object reference error here, I think.
I believe it is good practice in VBA to enforce variable declaration with Option Explicit:
https://riptutorial.com/excel-vba/example/3554/always-use--option-explicit-
https://learn.microsoft.com/en-us/office/vba/Language/Reference/User-Interface-Help/option-explicit-statement
Related
In worksheet wsb,I am trying to copy column B and Column having ParName in header and pasting it to columns B & H respectively of worksheet wso. The problem is It's running only for first Item and also for the first matched value of i for that item and not for all the matched item-i values.
Dim ws, wsa, wsb, wsc, wso As Worksheet
Dim index1b, LastRow, MOLastRow, wsoLastRow As Long
Dim ColLtr1b As Variant
Dim MoNameArr
Set wsb = Workbooks(Y).Sheets("REF")
wsb.Activate
LastRow = GetLastRow(wsb, 2)
Arr = Array("Abc", "Def")
Set wso = Workbooks(W).Sheets("Output")
For Each Item In Arr
For i = 2 To LastRow
If Cells(i, 2).Value = Item Then
wsb.Activate
ParName = wsb.Cells(i, 3).Value
Set wsc = Workbooks(M).Sheets(Item)
wsc.Activate
index1b = Application.Match(ParName, wsc.Rows(1), 0)
If Not IsError(index1b) Then
ColLtr1b = Replace(wsc.Cells(1, index1b).Address(True, False), "$1", "")
MOLastRow = wsc.Cells(Rows.Count, 2).End(xlUp).Row
Range("B2:B" & GetLastRow(wsc, 2)).Copy
wso.Activate
wsoLastRow = GetLastRow(wso, 2)
Range("B" & wsoLastRow + 1).Select
ActiveSheet.Paste
wsc.Activate
Range(ColLtr1b & "2:" & ColLtr1b & GetLastRow(wsc, 2)).Copy
wso.Activate
Range("H" & wsoLastRow + 1).Select
ActiveSheet.Paste
End If
End If
Next i
Next Item
Declare your variables like this:
Dim ws As Worksheet, wsa As worksheet, wsb as Worksheet
Dim wsc as Worksheet, wso As Worksheet
Dim index1b as Long, LastRow as Long, MOLastRow as Long, wsoLastRow As Long
Then start debugging with pressing F8. It goes line by line and you may see where is the problem in the nested loop. It can be in one of these 3:
you need to write Trim(Cells(i, 2)) in the If Cells(i, 2).Value = Item Then condition;
you are not calculating LastRow correctly;
you have On Error Resume Next somewhere in your code and you are entering an error w/o noticing;
I have macro that is attempting to check a column to see if there are any blank cells. In any blank cells I need to place an array formula.
The array formula functions fine on sheet and it is:
=INDEX($CJ$1:$CJ$100,MATCH(1,INDEX(($CL$1:$CL$100=CL2)*($CJ$1:$CJ$100<>""),0),0))
In the macro I cannot use fixed ranges so CJ100 and CL100 Need to be CL "LastRow" and CJ "LastRow" as the data ranges are dynamic.
Thus far I have this code:
Dim ws as worksheet
Dim LastRow as long
Dim i As Long
set ws = worksheets("Sheet1")
With ws
LastRow = ws.Range("B" & rows.Count).End(xlUp).Row
For i = 1 To LastRow
If ws.Range("CJ" & i).Value = "" Then
Range("CJ" & i).FormulaArray = "=index(CJ1:CJ ""& LastRow"",MATCH(1,INDEX((CL1:CL ""& LastRow"" = CL ""&i"")*(CJ1:CJ ""& LastRow <>""""),0),0))"
End If
Next i
End With
I am getting an error on this line: Range("CJ" & i).FormulaArray = "=index(CJ1:CJ ""& LastRow"",MATCH(1,INDEX((CL1:CL ""& LastRow"" = CL ""&i"")*(CJ1:CJ ""& LastRow <>""""),0),0))"
The error is the dreaded "Unable to set the FormulaArray property of the Range class".
After doing some research, it appeared that because I am applying to formula to one cell at a time, perhaps I do not need to use .FormulaArray I change the problem line to use .formula = "-=.... but this resulted in a "Application defined or object defined error" message.
Thinking the issue may be with my use of range I tried the following variation:
Dim LastRow as long
Dim ws as worksheet
set ws = worksheets("Sheet1")
LastRow = ws.Range("B" & rows.Count).End(xlUp).Row
Set rng = ws.Range("CJ2:CJ" & LastRow)
For Each cell In rng
'test value in column CJ
If cell.Value = "" Then
'inserts formula
cell.FormulaArray = "=index(CJ1:CJ ""& LastRow"",MATCH(1,INDEX((CL1:CL ""& LastRow"" = CL2)*(CJ1:CJ ""& LastRow <>""""),0),0))"
'cell.arrayformula
End If
Next
End With
This results in the same Formula Array error as my code at the top of the post.
I don't believe the formula is over 250 characters unless I am counting characters incorrectly? Perhaps there is a syntax error somewhere?
Your implementation of Array formula is incorrect and you have also not qualified the range reference correctly as preceding dot is missing.
Range("CJ" & i).FormulaArray = "=index(CJ1:CJ ""& LastRow"",MATCH(1,INDEX((CL1:CL ""& LastRow"" = CL ""&i"")*(CJ1:CJ ""& LastRow <>""""),0),0))"
Here's updated code which should work for you:
Sub ApplyArrayFormula()
Dim ws As Worksheet
Dim LastRow As Long
Dim i As Long
Set ws = Worksheets("Sheet1")
With ws
LastRow = .Range("B" & Rows.Count).End(xlUp).Row
For i = 1 To LastRow
If .Range("CJ" & i).Value = "" Then
.Range("CJ" & i).FormulaArray = "=INDEX($CJ$1:$CJ$" & LastRow & ",MATCH(1,INDEX(($CL$1:$CL$" & LastRow & "=CL" & i & ")*($CJ$1:$CJ$" & LastRow & "<>""""),0),0))"
End If
Next i
End With
End Sub
Anybody please help me figure my problem out?
Dim attPresent as Variant ' attpresent()
Set ws = thisworkbook.sheets("Sheet1")
lastrow = ws.cells(Rows.count, 8).end(xlup).row
attPresent = ws.Range("H4:H" & lastrow).Value 'errors if I use Dim attPresent() As Variant
For k = LBound(attPresent, 1) To UBound(attPresent, 1) ' Dim attPresent As Variant'errors if I use
msgbox attpresent(k,1)
Next
This line attPresent = ws.Range("H4:H" & lastrow).Value returns an error if I declare the variable as Dim attPresent() As Variant. Whereas, if declare the variable as Dim attPresent As Variant, this line For k = LBound(attPresent, 1) To UBound(attPresent, 1) errors.
Can anyone please help me clear this out?Thanks
As a good practice, try to remember to use Option Explicit, and also declare all your variables.
When you use Dim attPresent() As Variant to declare you array , and later on you insert values from a Range to your Array with attPresent = .Range("H4:H" & lastrow).Value, it will automatically Redim your array to 2-dimensinal array (1 to Row number, 1 to Column Number).
Option Explicit
Sub RngtoArray()
Dim attPresent() As Variant
Dim ws As Worksheet
Dim lastrow As Long
Dim k As Long
Set ws = ThisWorkbook.Sheets("Sheet1")
With ws
lastrow = .Cells(.Rows.Count, 8).End(xlUp).Row
attPresent = .Range("H4:H" & lastrow).Value
End With
For k = 1 To UBound(attPresent, 1)
MsgBox attPresent(k, 1)
Next
End Sub
Edit 1: A slightly different approach, in case there is only 1 cell in the Range:
With ws
lastrow = .Cells(.Rows.Count, 8).End(xlUp).Row
' for single column only - create a 1-Dimension array
ReDim attPresent(1 To lastrow - 4 + 1) ' when the Range starts from "H4"
For k = 1 To UBound(attPresent)
attPresent(k) = .Cells(4 + k - 1, "H")
Next k
End With
For k = 1 To UBound(attPresent)
MsgBox attPresent(k)
Next
I tried to separate the stuff that you had already defined but for clarity I thought I'd provide my full code:
Sub test()
Dim lastrow, i As Integer
Dim ws As Worksheet
Set ws = ActiveWorkbook.Worksheets("Sheet1")
Dim attPresent() As Variant
lastrow = ws.Cells(Rows.Count, "H").End(xlUp).Row
ReDim attPresent(lastrow - 4)
For i = 4 To lastrow
attPresent(i - 4) = ws.Range("H" & i).Value
Next
msg = Join(attPresent, " ")
MsgBox "The array holds: " & vbNewLine & msg
End Sub
I defined the array without a size to begin with then redefined it to the size it needs to be at a later stage once you know the lastrow (as you started on 4 i deducted 4 from lastrow).
I guessed the msgBox was to test what you had gathered so I created a dump that prints them all into one box but obviously change that if you have a lot of data. xD
To work with arrays I always loop through each individual entry, storing them one at a time. I'm not even sure whether you can dump an entire range into one in one step as I've never even looked into it. Anyway, I hope this solves your problem kupo.
Function RangeToArray(rng As Range)
Dim myArray() As Variant, ws As Worksheet
fr = rng.Row
fc = rng.Column
r = rng.Rows.Count
c = rng.Columns.Count
Set ws = rng.Worksheet
ReDim myArray(r - 1, c - 1)
For i = 0 To r - 1
For j = 0 To c - 1
myArray(i, j) = ws.Cells(fr + i, fc + j).Value2
Next j
Next i
RangeToArray = myArray
End Function
Sub f()
Dim rng As Range, attPresent() As Variant ' attpresent()
Set ws = ThisWorkbook.ActiveSheet 'Sheets("Sheet1")
lastrow = ws.Cells(Rows.Count, 8).End(xlUp).Row
Set rng = ws.Range("H4:H" & lastrow)
attPresent = RangeToArray(rng)
For k = LBound(attPresent, 1) To UBound(attPresent, 1) ' Dim attPresent As Variant'errors if I use
MsgBox attPresent(k, 0)
Next
End Sub
I created a more generic function that you can call in this specific case as well.
I want to optimize the following code, as it is very slow.
I am using the code found in this answer:
https://stackoverflow.com/a/27108055/1042624
However, it is very slow when looping through +10k rows. Is it possible to optimize my code below? I have tried to modify it a bit, but it does not seem to work.
Sub DeleteCopy2()
Dim LastRow As Long
Dim CurRow As Long
Dim DestLast As Long
Dim strSheetName As String
Dim arrVal() As Long
Application.ScreenUpdating = False
Application.Calculation = xlManual
strSheetName = "Week " & ISOWeekNum(Format(Date)) - 1
LastRow = Sheets("MatchData").Range("A" & Rows.Count).End(xlUp).Row
DestLast = Sheets(strSheetName).Range("A" & Rows.Count).End(xlUp).Row
ReDim arrVal(2 To LastRow) ' Headers in row 1
For CurRow = LBound(arrVal) To UBound(arrVal)
If Not Sheets(strSheetName).Range("A2:A" & DestLast).Find(Sheets("MatchData").Range("A" & CurRow).Value, LookIn:=xlValues, LookAt:=xlWhole) Is Nothing Then
Sheets("MatchData").Range("A" & CurRow).Value = ""
Else
End If
Next CurRow
Application.ScreenUpdating = True
Application.Calculation = xlCalculationAutomatic
End Sub
Can you try this for me? I have commented the code so that you will not have a problem understanding it. Also check how much time it takes for 10k+ rows
Logic
Store search values in array 1
Store destination values in array 2
Loop through the first array and check if it is present in the second array. If present, clear it
Clear the search values from sheet1
Output the array to the sheet1
Sort Col A so that the blanks go down.
Code
Sub Sample()
Dim wbMatch As Worksheet, wbDestSheet As Worksheet
Dim lRow As Long, i As Long
Dim MArr As Variant, DArr As Variant
Dim strSheetName As String
Dim rng As Range
strSheetName = "Sheet2" '"Week " & IsoWeekNum(Format(Date)) - 1
'~~> Set your worksheets
Set wbMatch = Sheets("MatchData")
Set wbDestSheet = Sheets(strSheetName)
'~~> Store search values in 1st array
With wbMatch
lRow = .Range("A" & .Rows.Count).End(xlUp).Row
Set rng = .Range("A2:A" & lRow)
MArr = rng.Value
End With
'~~> Store destination values in the 2nd array
With wbDestSheet
lRow = .Range("A" & .Rows.Count).End(xlUp).Row
DArr = .Range("A2:A" & lRow).Value
End With
'~~> Check if the values are in the other array
For i = LBound(MArr) To UBound(MArr)
If IsInArray(MArr(i, 1), DArr) Then MArr(i, 1) = ""
Next i
With wbMatch
'~~> Clear the range for new output
rng.ClearContents
'~~> Output the array to the worksheet
.Range("A2").Resize(UBound(MArr), 1).Value = MArr
'~~> Sort it so that the blanks go down
.Columns(1).Sort Key1:=.Range("A2"), Order1:=xlAscending, Header:=xlYes, OrderCustom:=1, _
MatchCase:=False, Orientation:=xlTopToBottom, DataOption1:=xlSortNormal
End With
End Sub
'~~> function to check is a value is in another array
Function IsInArray(stringToBeFound As Variant, arr As Variant) As Boolean
Dim j As Long
For j = 1 To UBound(arr, 1)
On Error Resume Next
IsInArray = Application.Match(stringToBeFound, Application.Index(arr, , i), 0)
On Error GoTo 0
If IsInArray = True Then Exit For
Next
End Function
Edit
Another way. Based on the sample file, this code runs in approx 1 minute.
Start : 8/4/2016 08:59:36 PM
End : 8/4/2016 09:00:47 PM
Logic:
It uses CountIf to check for duplicates and then deletes the duplicates using .Autofilter
Sub Sample()
Dim wbMatch As Worksheet, wbDestSheet As Worksheet
Dim lRow As Long
Dim strSheetName As String
Dim rng As Range
Debug.Print "Start : " & Now
strSheetName = "Week " & ISOWeekNum(Format(Date)) - 1
'~~> Set your worksheets
Set wbMatch = Sheets("MatchData")
Set wbDestSheet = Sheets(strSheetName)
'~~> Store search values in 1st array
With wbMatch
lRow = .Range("A" & .Rows.Count).End(xlUp).Row
.Columns(2).Insert
Set rng = .Range("B2:B" & lRow)
lRow = wbDestSheet.Range("A" & wbDestSheet.Rows.Count).End(xlUp).Row
rng.Formula = "=COUNTIF('" & strSheetName & "'!$A$1:$A$" & lRow & ",A2)"
DoEvents
rng.Value = rng.Value
.Range("B1").Value = "Temp"
'Remove any filters
.AutoFilterMode = False
With .Range("A1:E" & lRow) 'Filter, offset(to exclude headers) and delete visible rows
.AutoFilter Field:=2, Criteria1:=">0"
.Offset(1, 0).SpecialCells(xlCellTypeVisible).EntireRow.Delete
End With
'Remove any filters
.AutoFilterMode = False
.Columns(2).Delete
End With
Debug.Print "End : " & Now
End Sub
Looks like #SiddarthRout and I were working in parallel...
My code example below executes in less than 2 secs (eyeball estimate) over almost 12,000 rows.
Option Explicit
Sub DeleteCopy2()
Dim codeTimer As CTimer
Set codeTimer = New CTimer
codeTimer.StartCounter
Dim thisWB As Workbook
Dim destSH As Worksheet
Dim matchSH As Worksheet
Set thisWB = ThisWorkbook
Set destSH = thisWB.Sheets("Week 32")
Set matchSH = thisWB.Sheets("MatchData")
Dim lastMatchRow As Long
Dim lastDestRow As Long
lastMatchRow = matchSH.Range("A" & matchSH.Rows.Count).End(xlUp).Row
lastDestRow = destSH.Range("A" & matchSH.Rows.Count).End(xlUp).Row
'--- copy working data into memory arrays
Dim destArea As Range
Dim matchData As Variant
Dim destData As Variant
matchData = matchSH.Range("A1").Resize(lastMatchRow, 1)
Set destArea = destSH.Range("A1").Resize(lastDestRow, 1)
destData = destArea
Dim i As Long
For i = 2 To lastDestRow
If Not InMatchingData(matchData, destData(i, 1)) Then
destData(i, 1) = ""
End If
Next i
'--- write the marked up data back to the worksheet
destArea = destData
Debug.Print "Destination rows = " & lastDestRow
Debug.Print "Matching rows = " & lastMatchRow
Debug.Print "Execution time = " & codeTimer.TimeElapsed & " secs"
End Sub
Private Function InMatchingData(ByRef dataArr As Variant, _
ByRef dataVal As Variant) As Boolean
Dim i As Long
InMatchingData = False
For i = LBound(dataArr) To UBound(dataArr)
If dataVal = dataArr(i, 1) Then
InMatchingData = True
Exit For
End If
Next i
End Function
The timing results from my code are (using the timer class from this post ):
Destination rows = 35773
Matching rows = 23848
Execution time = 36128.4913359179 secs
I have the routine below where I'm copying all the contents of a sheet if the sheet name matches the array name.
I've got the sheets copying to the destination, but I'm not getting the array value curRow to increment.
What am I missing here?
Sub test()
Dim curRow As Integer, CurrentRow As Integer, LastRow As Integer, LastRow2 As Integer
Dim activeWorksheet As Worksheet
Set activeWorksheet = ActiveSheet
Dim ws As Worksheet
Dim arArray As Variant
Sheets("Total Tabs").Activate
arArray = Sheets("Total Tabs").Range("A1", Range("A" & Rows.Count).End(xlUp))
LastRow = ActiveSheet.Range("A" & Rows.Count).End(xlUp).Row
curRow = 1
CurrentRow = 2
For curRow = 1 To LastRow
For Each ws In ActiveWorkbook.Worksheets
' If curRow <> 1 Then
' curRow = curRow + 1
' End If
If ws.name = arArray(curRow, 1) Then
LastRow2 = ws.Range("A" & Rows.Count).End(xlUp).Row + 1
For CurrentRow = 2 To LastRow2
ws.Range("A" & CurrentRow & ":N" & CurrentRow).Copy Destination:=Sheets("Reps No Longer Here").Range("A" & CurrentRow)
CurrentRow = CurrentRow + 1
Next
curRow = curRow + 1
End If
Next ws
Next curRow
End Sub
UPDATE:
Here is the final code that I have and works as it should. Included is also the ability to hide the tab once it has been processed.
I'm sure it can be optimized, but here it is:
Sub CombineDataToRNLH()
Dim curRow As Integer, CurrentRow As Integer, LastRow As Integer, LastRow2 As Integer
Dim activeWorksheet As Worksheet
Set activeWorksheet = ActiveSheet
Dim ws As Worksheet
Dim arArray As Variant
Dim pasterow As Integer
Dim RepName As String
'Activate the sheet with the list and then read the list of names
'straight into an array
Sheets("Total Tabs").Activate
arArray = Sheets("Total Tabs").Range("A1", Range("A" & Rows.Count).End(xlUp))
'Find last element in the array and calculate as rows
LastRow = ActiveSheet.Range("A" & Rows.Count).End(xlUp).Row
curRow = 1 'Index for evaluating array elements
CurrentRow = 2 'Counter for use in processing all rows in matched sheet to destination sheet
LastRow2 = 1 'Find number of rows in the matched tab
pasterow = 2 'Counter to ensure that I'm always copying data to the first available row
'Set up loop so that I can match array elements to individual sheet names
For curRow = 1 To LastRow
For Each ws In ActiveWorkbook.Worksheets
If ws.name = arArray(curRow, 1) Then
LastRow2 = ws.Range("A" & Rows.Count).End(xlUp).Row
For CurrentRow = 2 To LastRow2
ws.Range("A" & CurrentRow & ":N" & CurrentRow).Copy _
Destination:=Sheets("Reps No Longer Here").Range("A" & pasterow)
If CurrentRow = LastRow2 Then
curRow = curRow + 1
pasterow = pasterow + 1
ws.Visible = xlSheetVeryHidden 'Set it to very hidden.
Exit For
End If
pasterow = pasterow + 1
Next
End If
Next ws
Next curRow
Sheets("How To").Activate
End Sub
I think your code can be simplified and streamlined a bit. From what I gather, you want to loop through some sheets (as defined in your aaArray variable) and copy the data to a "Reps No Longer Here" tab. See if this does what you're after:
Sub test()
Dim LastRow As Long, _
LastRow2 As Long
Dim ws As Worksheet
Dim arArray As Variant
Dim sheetName As Variant
With Application
.ScreenUpdating = False
End With
With Sheets("Total Tabs")
arArray = .Range("A1", .Range("A" & Rows.Count).End(xlUp))
For Each sheetName In arArray
On Error Resume Next
Set ws = Sheets(sheetName)
On Error GoTo 0
If ws Is Nothing Then
' we don't need to do anything since the sheet doesn't exist
Else
LastRow2 = ws.Range("A" & Rows.Count).End(xlUp).Row
LastRow = Sheets("Reps No Longer Here").Range("A" & Rows.Count).End(xlUp).Row + 1
ws.Range("A2:N" & LastRow2).Copy Destination:=Sheets("Reps No Longer Here").Range("A" & LastRow)
End If
Next sheetName
End With
With Application
.ScreenUpdating = True
End With
End Sub