"Object variable or With block variable not set" in VBA - vba

I am looking to calculate a column (in wsOut) of averages using VBA. The input is in another sheet (wsRefor).
I use the following code, where I use the worksheet function to calculate the average
Dim Avg As Double
Dim AvgRange As Range
Set Reformulering = ActiveSheet
For i = 1 To lastCol
AvgRange = Range(wsRefor.Cells(1 + i, 4), wsRefor.Cells(1 + i, lastCol))
wsOut.Cells(i + 1, 4).Value = Application.WorksheetFunction.Average(AvgRange)
Next
Yet, I get the mistake, from the second line inside of the for-loop:
"Object variable or With block variable not set"
I am not sure I understand the error from videos I have watched and other forum discussion, so I am hoping anyone can explain or potentially point of the mistake

You need to use the Set keyword when you are assigning an object rather than a value.
A Range is an object and so it needs to be Set
Set AvgRange = Range(wsRefor.Cells(1 + i, 4), wsRefor.Cells(1 + i, lastCol))
To see the difference, you can do this:
Dim test As Variant
Range("A1").Value = "some text"
test = Range("A1") '// test is now a string containing "some text"
Set test = Range("A1") '// test is now a range object
MsgBox test.Value '// Displays the value of the range object "some text"

Assuming you defined Dim wsRefor As Worksheet, and set it to the right Sheet, then modify your line:
AvgRange = Range(wsRefor.Cells(1 + i, 4), wsRefor.Cells(1 + i, lastCol))
to:
Set AvgRange = wsRefor.Range(Cells(1 + i, 4), Cells(1 + i, lastCol))
or, on the safe side:
With wsRefor
Set AvgRange = .Range(.Cells(1 + i, 4), .Cells(1 + i, lastCol))
End With
Edit 1: full code which I've tested (also has error handling for the Average Function)
Option Explicit
Sub DynamicAvgRange()
Dim wsRefor As Worksheet
Dim wsOut As Worksheet
Dim Avg As Double
Dim AvgRange As Range
Dim lastCol As Long
Dim i As Long
Set wsRefor = ThisWorkbook.Sheets("Refor")
Set wsOut = ThisWorkbook.Sheets("Out")
' just for simulating the tests
lastCol = 6
For i = 1 To lastCol
With wsRefor
Set AvgRange = .Range(.Cells(1 + i, 4), .Cells(1 + i, lastCol))
End With
If Not IsError(Application.Average(AvgRange)) Then
wsOut.Cells(i + 1, 4).Value = Application.Average(AvgRange)
Else
' Average value returned an error (no values in the searched range)
wsOut.Cells(i + 1, 4).Value = "" ' put a blank value >> modify to your needs
End If
Next i
End Sub

Related

Select a part of Excel Cell (Not Complete Cell) and compare it to other cells

I have part of a code for VBA to run and add blank rows after it finds non-sequential numbers in my data. However my data contains letters and the way the code is written it only compares integer sequences. Is there a way to manipulate the code to only select part of the cell and compare that part?
For example I have the following sequence:
100-CG-000-10008
100-CG-000-10009
100-CG-000-10011
100-CG-000-10012
and the idea is to get VBA to insert a blank row after 100-CG-000-10009 due to the fact that it is no longer in sequence after that. The code I'm working with is:
Sub InsertNullBetween()
'Update 20130829
Dim WorkRng As Range
Dim Rng As Range
Dim outArr As Variant
Dim dic As Variant
Set dic = CreateObject("Scripting.Dictionary")
'On Error Resume Next
xTitleId = "KutoolsforExcel"
Set WorkRng = Application.Selection
Set WorkRng = Application.InputBox("Range", xTitleId, WorkRng.Address, Type:=8)
num1 = WorkRng.Range("A1").Value
num2 = WorkRng.Range("A" & WorkRng.Rows.Count).Value
interval = num2 - num1
ReDim outArr(1 To interval + 1, 1 To 2)
For Each Rng In WorkRng
dic(Rng.Value) = Rng.Offset(0, 1).Value
Next
For i = 0 To interval
If dic.Exists(i + num1) Then
outArr(i + 1, 1) = i + num1
outArr(i + 1, 2) = dic(i + num1)
Else
outArr(i + 1, 1) = ""
outArr(i + 1, 2) = ""
End If
Next
With WorkRng.Range("A1").Resize(UBound(outArr, 1), UBound(outArr, 2))
.Value = outArr
.Select
End With
End Sub
Is there any way I can change the code to only look at the last part of the cell containing the integers after the 3rd "-"?
edit with a code to only insert lines and handle whatever gap
you could use this
Option Explicit
Sub InsertNullBetween()
Dim i As Long, gap As Long
With Range("A1", Cells(Rows.count, 1).End(xlUp))
For i = .Rows.count To 2 Step -1
gap = Right(.Cells(i), 5) - Right(.Cells(i - 1), 5)
If gap > 1 Then .Cells(i).Resize(gap - 1).Insert xlDown
Next
End With
End Sub
Your code looks too complicated for the task. Here I will only give you the way to extract the last number following the last "-" from your cell:
Function ExtractNumber(r As Range) As Long
ExtractNumber = Mid(r.Value, 1 + InStrRev(r.Value, "-"))
End Function
Supposing your current code works well if those cells where pure numbers, you can use this function to customize it. For example:
num1= ExtractNumber(WorkRng.Range("A1"))
num2 = ExtractNumber(WorkRng.Range("A" & WorkRng.Rows.Count))
...
dic(ExtractNumber(Rng)) = Rng.Offset(0, 1).Value
etc..

Object variable not set

I keep on getting an "object variable not set" error with this code. I want to offset the startcell cell address to take the average of a corresponding column. However, vba tries to offset the value of the startcell, rather than the cell address. It would be great if someone can offer me some guidance. Thanks!
Sub Macro1()
Dim i As Integer
Dim rowcount As Integer
Dim startcell As Range
startcell = ActiveSheet.Cells(2, 3).Address(False, False)
rowcount = Range("C2", Range("C2").End(xlDown)).Count
For i = 1 To rowcount
If Not Cells(i, 3).Value = Cells(i + 1, 3).Value Then
startcell.Offset(0, 11).Value = Application.WorksheetFunction.Average( _
Range(startcell.Offset(0, 8), Cells(i, 11)))
startcell = ActiveSheet.Cells(i + 1, 3).Address(False, False)
End If
Next i
End Sub
To save a given range in a variable, you either use its address, which is a string, or a Range object. The latter is usually preferred, and it looks like this was you intent.
Dim startcell As Range
....
Set startcell = ActiveSheet.Cells(2, 3) ' <-- Set a range object variable
....
Set startcell = ActiveSheet.Cells(i + 1, 3) ' <-- Set
On the other hand if you want to use the address (less recommended unless there are specific reasons):
Dim startAddr As String ' <--
....
startAddr = ActiveSheet.Cells(2, 3).Address(False, False) ' <-- ok, save address
....
Range(startAddr).Offset(0, 11).Value = ... ' Range(startAddr) reconstructs a range from the address
startAddr = ActiveSheet.Cells(i + 1, 3).Address(False, False)

ERROR 13 !! Type mismatch

ANY IDEA ??? I don't know why there's a type error especially that i've changed the type of both cells
Cells(i, 7).NumberFormat = "#"
Workbooks("template.xls").Worksheets("Introduction").Cells(j, 21).NumberFormat = "#"
If Left(Cells(i, 7), 13) = Workbooks("template.xls").Worksheets("Introduction").Cells(j, 21).Value
.
.
.
Code from comments:
Dim i As Long
Dim j As Integer
For j = 5 To derlig
For i = 2 To 4000
Cells(i, 2).NumberFormat = "#"
Workbooks("template.xls").Worksheets("Introduction").Cells(j, 21).NumberFormat = "#"
Workbooks("Cat export.xls").Worksheets("Items").Activate
If Left(Cells(i, 2), 13).Value = Workbooks("template.xls").Worksheets("Introduction").Cells(j, 21).Value Then
Workbooks("Cat export.xls").Worksheets("Items").Cells(i, 3) = Right(Workbooks("Cat export.xls").Worksheets("Items").Cells(i, 2), 5)
End If
Next
Next
The error is happening because you are not qualify all of your objects, so if any workbook or worksheet is active other than what you may expect, the code may not perform correctly.
It's best practice to always qualify objects in VBA and work directly with them.
See below:
Dim wbMyWB as Workbook, wbTemplate as Workbook
Set wbMyWB = Workbooks("myWB.xlsx") 'change as needed
Set wbTemplate = Workbooks("template.xls")
Dim wsMyWS as Worksheet, wsIntro as Worksheet
Set wsMyWS = wbMyWB.Worksheets("Sheet1") ' change as needed
Set wsIntro = wbTemplate.Worksheets("introduction")
'....
Dim rMyRange as Range, rIntro as Range
'assumes i and j are properly set to long or integer (or variant (hopefully not) type
'ideally i and j are both set to integer
rMyRange = wsMyWs.Cells(i,7)
rIntro = wsIntro.Cells(j,21)
rMyRange.NumberFormat = "#"
rIntro.NumberFormat = "#"
If Left(rMyRange,13).Value = rIntro.Value Then
'condition satisfied
End If

Referencing worksheets to draw data

I am writing a vba macro to allow me to reference data from a worksheet and summarize some of the data rather than using a ton of formulas to do so.
I am having difficulties in referencing worksheets and have reverted to activating sheets. I'm not sure what I am doing incorrectly. For example:
Sheets("Rainfall").Activate
Set x = Range(Range("C2"), Range("C2").End(xlDown))
rather than
Set x = Sheets("Rainfall").Range(Range("C2"), Range("C2").End(xlDown))
When I attempt to reference code such as
Cells(2 + j, 3) = Application.WorksheetFunction.VLookup(Cells(2 + j, 2), Worksheets("Raw Data").Range(Range("C4"), Range("H4").End(xlDown)), 6, False)
I get a 1004 error. Below is my code and if anyone has any suggestions on the simplification of the code that would be great as well.
Sub selectall()
Dim x, y As Range
Dim nv, rd As Long
Set Wkb = Workbooks("DWH Calculations V1.xlsm")
Sheets("Rainfall").Activate
Set x = Range(Range("C2"), Range("C2").End(xlDown))
nv = x.Rows.Count
'MsgBox (nv)
Sheets("Raw Data").Activate
Set y = Range(Range("E4"), Range("E4").End(xlDown))
rd = y.Rows.Count
'MsgBox (rd)
MinD = Round(Application.WorksheetFunction.Min(y), 0)
MaxD = Round(Application.WorksheetFunction.Max(y), 0)
Ndays = MaxD - MinD
'MsgBox (Ndays)
Sheets("Rainfall").Activate
Cells(2, 2) = MinD
For j = 1 To Ndays - 1
Cells(2 + j, 2) = Cells(1 + j, 2) + 1
Cells(2 + j, 3) = Application.WorksheetFunction.VLookup(Cells(2 + j, 2), Worksheets("Raw Data").Range(Range("C4"), Range("H4").End(xlDown)), 6, False)
Next j
End Sub
Thank you all for your help
This has been asked many times before - you need to qualify all the Range calls with a worksheet object, so:
Set x = Sheets("Rainfall").Range(Sheets("Rainfall").Range("C2"), Sheets("Rainfall").Range("C2").End(xlDown))
or use a With...End With block:
With Sheets("Rainfall")
Set x = .Range(.Range("C2"), .Range("C2").End(xlDown))
End With
and note the periods before all three Range calls. You can also use a Worksheet variable:
Dim ws as Worksheet
Set ws = Sheets("Rainfall")
Set x = ws.Range(ws.Range("C2"), ws.Range("C2").End(xlDown))
The problem is the range-within-range:
replace:
Set x = Range(Range("C2"), Range("C2").End(xlDown))
with:
With Sheets("Rainfall")
Set x = .Range(.Range("C2"), .Range("C2").End(xlDown))
End With
Activate is not needed to Set ranges.

Output Array of Strings onto new range

I'm attempting to grab strings from a specific range of one worksheet and output the array onto another sheets range. Unfortunately the resulting code gives me no output of any sort.
Thank you for the help in advance.
Dim strFunds As String
Dim varItemName As Variant
Dim strItemName() As String
Dim iRow As Long
Dim rngMyRange As Range
Sub DataGrab()
ActiveWorkbook.Sheets("Returns").Activate
Set rngMyRange = Range("A5:A100")
varItemName = rngMyRange
ReDim strItemName(LBound(varItemName, 1) To UBound(varItemName, 1))
ActiveWorkbook.Sheets("Data").Activate
Range("A3:A" & UBound(strItemName) + 1) = WorksheetFunction.Transpose(strItemName)
End Sub
Sub Main()
Dim rngArr
rngArr = Sheets("Returns").Range("A5:B100").Value
Sheets("Data").Range("A3").Resize(UBound(rngArr) + 1, 2) = rngArr
End Sub
EDIT: Oops didn't see me how's answer above. This answer is pretty much the same thing.
Try this.
First, change varItemName to an array:
Dim varItemName() As Variant
Then:
Sub DataGrab()
ActiveWorkbook.Sheets("Returns").Activate
Set rngMyRange = Range("A5:A100")
varItemName = rngMyRange.Value
'ReDim strItemName(LBound(varItemName, 1) To UBound(varItemName, 1))
ActiveWorkbook.Sheets("Data").Activate
Range("A3").Resize(1, UBound(varItemName) + 1) = WorksheetFunction.Transpose(varItemName)
End Sub
That is assuming you want to convert your columnar data into a single row. If not, do this on the last line instead:
Range("A3").Resize(UBound(varItemName) + 1, 1) = varItemName
You are not assigning any values to strItemName. Here's a version that just keeps everything in varItemName.
ActiveWorkbook.Sheets("Returns").Activate
Set rngMyRange = Range("A5:A100")
varItemName = rngMyRange.Value
ActiveWorkbook.Sheets("Data").Activate
Range(Cells(3, 1), Cells(3, UBound(varItemName))) = WorksheetFunction.Transpose(varItemName)
UPDATE: If you don't need to save varItemName, you can use this shorter version:
Set rngMyRange = Range("A5:A100")
ActiveWorkbook.Sheets("Data").Activate
Range(Cells(3, 1), Cells(3, 100)) = WorksheetFunction.Transpose(rngMyRange)