Simple excel macro - excel

I have just started to work on Excel macros.
Please let me know what is wrong in the following code:
Sub color_red()
Dim range As range
For R = 1 To 20
If Cells(R, 1).Value = 1 Then range(R, R).Interior.ColorIndex = 3
Next R
End Sub
Error message displayed:
Object variable or with block variable not set
Thanks in advance for your guidance.

You can't use variable name which is any VBA: key word, function, object name, collection name, etc. Try to change your variable name from range into rngTable.
EDIT after additional information written as comments in different areas of that post:
change your if code line into:
If Cells(R,1).Value = 1 Then Cells(R,1).EntireRow.Interior.ColorIndex =3
which will colour all sheet columns in row R. To colour certain range, e.g. in 10 columns you could use this line instead of your if:
If Cells(R,1).Value = 1 Then Cells(R,1).Resize(1,10).Interior.ColorIndex =3

I see that you are still having problem with this code. Please try this simple subroutine, this might be what you need:
Public Sub ColorRowIf()
Dim r As Long
For r = 1 To 20
If Cells(r, 1).value = 1 Then
Cells(r, 1).EntireRow.Interior.ColorIndex = 3
End If
Next r
End Sub
Please also note that using 'Cells()' is not a good way to go, but I have included it for simplicity (as asked). 'Cells()' will always refer to the active sheet cells which might not always be the case. So, instead you should use full reference of the sheet (by code name method) -- so in this example it should say for e.g.: Sheet1.Cells(r,1).Value = 1. Also, remember Sheet1 is not the tab name, its the code name that you see in project window.

#KazJaw is correct, you cannot use a key word as a variable name. But the range or rngTable variable is still used before it is set.
You could still use a Range variable, but why don't you use the same way of addressing as in the if statement?
If Cells(r, 1).Value = 1 Then Cells(r, 1).Interior.ColorIndex = 3
Otherwise you would have to set the range object before you use it:
Set rngTable = Cells(r, 1)

I feel like a lot of people gleaned over the fact your variable 'R' is not defined other than 666bytes.
Declaring the variables you are going to be using is an important thing to do in order to get code to work.

Instead of range you can use the Cells function. You have to refer the WorkSheet for using range. Since nothing is mentioned ,its throwing Object variable or with block variable not set.
Sub color_red()
Dim r As Integer
For r = 1 To 20
If Cells(r, 1).Value = 1 Then Cells(r, r).Interior.ColorIndex = 3
Next r
End Sub

Related

VBA Runtime Error 1004 "Application-defined or Object-defined error" when trying to assign values to a range

I am not sure why I am getting this error when trying to run the code below
Sub introtocollection()
Dim i As Integer
Dim coll As New Collection
Dim r As Range
Set r = Worksheets("Sheet1").Range(Cells(1, 1), Cells(5, 2))
For i = 2 To 5
coll.Add r.Cells(i, 2)
Next i
For i = 2 To 5
Worksheets("Sheet1").Range(Cells(i, 6)).Value = coll(i).Value
Next i
End Sub
The error is highlighted in this code line below
Worksheets("Sheet1").Range(Cells(i, 6)).Value = coll(i).Value
try Worksheets("Sheet1").Range(Cells(i, 6)).Value = coll(i,2).Value
it may help
This error most likely arose due to the fact that the code is trying to reach some area indirectly by the address or name in Cells(i,6) (see how the first parameter is interpreted in Range). Suppose you put a string "A1" into the cell F2 which is Cells(2, 6). Then the code Range(Cells(2,6)) will substitute Cells(2,6) by its value and return Range("A1").
I assume that this wasn't your intent. I guess that the Cells(i,6) are empty for i=2 to 5 or do not contain correct range addresses. Maybe you wanted to do something like this:
Worksheets("Sheet1").Cells(i, 6).Value = coll(i).Value
If so, then pay attention to how the assignment of the Collection keys is provided, because you have another trap on your way. If you omit declaring keys explicitly, then they are assigned automatically starting from 1. So the next error you'll get is Subscript out of range in the second for-loop at i=5. To avoid this, declare the keys explicitly:
coll.Add r.Cells(i, 2), Key:=i
P.S.
If, nevertheless, you wanted to do exactly indirect access to cells, then you should check the correctness of addresses in the F2:F5 range. I can't imagine that anything else could've caused the error you mentioned.

Tables and loops VBA

I am new to coding and have written some code to do some calculations within a table and fill in columns. I have it working for the first row within the table but I am having some trouble figuring out how to loop it so that it completes the calculations for every row within the table. Any help and advice would be greatly appreciated!
UPDATE:
Thanks for the Help! The code works perfectly for the first part provided here, I have tried to apply this to the other 2 parts, but am coming up with an error. I think due to the fact that I am trying to use a string as the input? I have tried without the quotation marks but all it returns is "#NAME?".
Sub CommandButton1_Click()
Dim tbl As ListObject
Set tbl = ThisWorkbook.Sheets("Data").ListObjects("Table1")
Dim formulaText As String
formulaText =
"=IF([#Reach]>=100000,5,IF([#Reach]>=50000,3,IF([#Reach]>=10000,2,1)))"
tbl.ListColumns("Media Significance").DataBodyRange.Formula = formulaText
Dim formulaText1 As String
formulaText1 = "=IF([#Headline Mentions]>="Yes",5,IF([#Exclusive
Mentions]>="Yes",3,1))"
tbl.ListColumns("Prominence Score").DataBodyRange.Formula = formulaText1
Dim formulaText2 As String
formulaText2 = "=IF([#Sentiment]>="Very Positive",2,IF([#Sentiment]>="Very
Negative",2,1))"
tbl.ListColumns("Very Positive/ Very Negative").DataBodyRange.Formula =
formulaText2
End Sub
Looping through each cell in a range is very slow, so you're either going to want to either load your data into an array first, or use a regular Excel formula + the FillDown function.
In this particular case, I'd recommend the second option, which will allow you to add your formula to a single cell and fill it down the rest of the column. Something like this should work:
Dim colNum As Long
With ThisWorkbook.Sheets("Example Sheet")
'Find last row in sheet
Dim lastRow As Long
lastRow = .Cells(.Rows.Count, 1).End(xlUp).Row
'Add first formula
colNum = .Range("V2").Column
.Cells(2, colNum).Formula = "=IF(T2>=100000,5,IF(T2>=50000,3,IF(T2>=10000,2,1)))"
.Range(.Cells(2, colNum), .Cells(lastRow, colNum)).FillDown
End With
One problem with your current code is that the column letters are hard-coded. IE, you're expecting to find something called "Reach" in column L, and assuming that this will always be the case. However, if you ever add another column to the left of "Reach", it will break your code.
That's one reason why I'd probably recommend turning your range into a table object with descriptive column names. That should make your code much easier to read and maintain, like this:
Dim tbl As ListObject
Set tbl = ThisWorkbook.Sheets("Example Sheet").ListObjects("YourTable")
Dim formulaText As String
formulaText = "=IF([#Reach]>=100000,5,IF([#Reach]>=50000,3,IF([#Reach]>=10000,2,1)))"
tbl.ListColumns("Reach Analysis").DataBodyRange.Formula = formulaText
For starters, you have redundant criteria in your first If/ElseIf/End If statement.
This,
If Reach >= 100000 Then
Result = 5
ElseIf Reach < 100000 And Reach >= 50000 Then
Result = 3
ElseIf Reach < 50000 And Reach >= 10000 Then
Result = 2
ElseIf Reach < 10000 Then
Result = 1
End If
... can be written more succinctly as,
If Reach >= 100000 Then
Result = 5
ElseIf Reach >= 50000 Then
Result = 3
ElseIf Reach >= 10000 Then
Result = 2
Else
Result = 1
End If
These If/ElseIf/Else/End If conditions are resolved sequentially. Since you won't get into the second criteria unless Reach is less than 100000, there is no need to put that specification into the second criteria. The same logic can be applied for the remainder of the conditions.
Your second If/ElseIf/End If has an error in syntax.
ElseIf Headline = "No" And Exclusive = Yes Then
The Yes here should be quoted or the condition will be looking for a variable named Yes. Putting Option Explicit at the top of the module code sheet in the Declarations area will catch these errors quickly. You can also access the VBE's Tools, Options command and put a checkmark beside Require Variable Declaration and Option Explicit will be automatically put into the Declaration area of each new code sheet you create.

Excel says no "End Sub", and crashes just by moving cursor

I am writing a VBA code to go through a specified range or ranges, look for a keyword provided by the user at run-time, and grab the value in the cell offset from the cell with the keyword by an amount also provided by the user. For instance, if you wanted to look through A1:B10 for the word "Apple" and then grab the value in the cell to the right of every instance of "Apple", it can do that. Two weird things have been occurring for me. First and not so weird, when I run it and click the cancel button on the userform that only contains the single line "Unload Me", it throws an error saying it expected and End Sub statement, but it has one. I don't know why it is doing that. Weird thing number 2. Whenever I click and move the cursor to the end of the file after the Cancel_Click() sub, my excel crashes and closes. Every. Single. Time. And it is weird that it does that just from me clicking. It also sometimes happens when I click around the Cancel_Click() sub or hit enter around there too. Just simply from clicking. I don't get it. Any ideas? Code contained in the userform is below. Fyi, the user can input ranges like "A1:A10,E1:E10" separated by commas for multiple ranges. I don't think it is important for this question, but I thought I would add that since i don't know how to add the userform here, if you even can.
Private Sub Accept_Click()
'Searches for string input into the KeywordBox
'Grabs contents of the cell defined by the OffsetBox
'The range it searches through is defined by the RangeBox
Dim rawRange As String: rawRange = Me.RangeBox.Text
Dim rawOffset As String: rawOffset = Me.OffsetBox.Text
Dim Keyword As String: Keyword = Me.KeywordBox.Text
Dim numOfRanges As Integer: numOfRanges = 1
Dim Ranges() As Range
Dim commaLoc As Integer: commaLoc = -1
Dim tempRange As String: tempRange = rawRange
Dim offset As Integer
Dim values() As Double
Dim valCount As Integer: valCount = 0
'--------------------------------------------------------
'Set ranges
For i = 1 To Len(rawRange)
If (Mid(rawRange, i, 1) = ",") Then
numOfRanges = numOfRanges + 1
End If
Next
ReDim Ranges(numOfRanges) As Range
If (Not numOfRanges = 1) Then
For i = 1 To numOfRanges - 1
commaLoc = InStr(1, tempRange, ",")
Set Ranges(i) = Range(Left(tempRange, commaLoc - 1))
tempRange = Right(tempRange, Len(tempRange) - commaLoc)
Next
End If
Set Ranges(numOfRanges) = Range(tempRange)
'---------------------------------------------------------
'Set offset
If (IsNumeric(rawOffset)) Then
offset = CInt(rawOffset)
Else:
MsgBox ("Offset was not input as a number")
Exit Sub
End If
'----------------------------------------------------------
'Searches for keyword
For i = 1 To numOfRanges
For Each cell In Ranges(i)
If (cell.Value = Keyword) Then
valCount = valCount + 1
End If
Next
Next
ReDim values(valCount) As Double
valCount = 0
For i = 1 To numOfRanges
For Each cell In Ranges(i)
If (cell.Value = Keyword) Then
valCount = valCount + 1
values(valCount) = cell.offset(0, offset).Value
End If
Next
Next
For i = 1 To valCount
Range("I" & i).Value = values(i)
Next
Unload Me
End Sub
I've had similar, weird things happen to me. A good thing to try is to force the VBA project to reset, then save, exit, and restart Excel.
To force a project reset, add an Enum to the general section of one of your code modules. It doesn't matter what the enum is...make it something simple, like
Enum stoplight
Red
Yellow
Green
End Enum
As you do that, you'll get a message saying that it will reset your project. That's fine; let that happen. Then save your Excel workbook, exit excel completely, start it up again, reload your workbook, go into the VBA Editor, and delete the enum you added. Then recompile and see if things work better for you.
You put an "Exit Sub" in the set offset, this is probably causing your problem.
I was able to fix the issue by making a new workbook and copying everything over. It worked fine. I think the original was corrupted somehow. For those having the same issue, I think Rich Holton's answer would be worth a try in case you have more than just a few things to copy. Thanks everyone for you time and input on this!

VBA - Loop will not increment by one each time in ID column

In my VBA code, I am attempting to do the following:
Set the Active Cell to C11
Add the number 1 to an id
Prompt the user for a name and enter that in the cell to the right
Go to the next row & repeat.
However, the number that is entered is another one, not a 2, 3, 4....
Instead, I get the following:
1 Name 1
1 Name 2
1 Name 3
and I want:
1 Name 1
2 Name 2
3 Name 3
Here is the code, what am I missing?
Sub AddToSheet()
Dim id As Integer
Dim name As String
Worksheets("sheet1").Activate
ActiveCell.Range("C11").Select
For Each mycell In Range("C11:C20")
id = mycell.Select
ActiveCell.Value = 1
id = id + 1
name = mycell.Offset(0, 1).Select
name = InputBox("what is the film?")
ActiveCell.Value = name
Next mycell
End Sub
It's not what you're missing, it's what you are getting wrong - some pointers:
1) There is rarely (dare I say, never) a need to use .Select in Excel VBA, you can access an object's properties directly without selecting the actual object. This is generally considered bad practice.
2) id = mycell.Select is not a valid statement, the .Select method merely sets focus to an object(s) it is not used to return a value.
3) ActiveCell.Value = 1 <-- This is where you are going wrong as far as your question is concerned
4) Your code increments the value of id with each loop, but you do not actually use this value for anything - another hint at why it's not working as you expected.
5) Try and use indentation on your code, this will make it much easier for you (and others) to follow the logic of your code and help to ensure you have closed all 'block' statements.
Try this code instead:
Sub AddToSheet()
Dim i As Integer
For i = 11 To 20
Range("C" & i & ":D" & i).Value = Array(i - 10, InputBox("What is the film?"))
Next i
End Sub
This accesses the .Value method of the Range object without actually selecting or activating it, and so we skip a couple of lines of code straight away. Secondly - I've used an Array to assign the values so that we can do it all in one line of code - this is nothing groundbreaking and you won't see any difference in speed/performance but it's hopefully something for you to look at and possibly manipulate for your own uses in the future.

How to make absolute cell ref in loop work and skipping over a column in loop?

I ALMOST got my code working but there are still two things wrong with it (two major things anyway).
1) The absolute cell ref. is not working as it does in Excel. I want for example $A5 but instead of changing to A6 A7 etc., it stays A5 throughout the loop.
2) There is a third column that I need to skip over. I only need my loop to write to columns under VOL and CAP, not %UTIL. How can I tell my loop to skip over $UTIL?
Option Explicit
Dim myRange As Range
Function numberOfRows() As Integer
Debug.Print ("Start test")
ThisWorkbook.Worksheets("LCI").Range("A9").Select
Set myRange = Range(Selection, Selection.End(xlDown))
Debug.Print ("Rows: " & myRange.Rows.Count)
numberOfRows = (myRange.Rows.Count)
End Function
Function numberOfColumns() As Integer
Debug.Print ("Start test")
ThisWorkbook.Worksheets("LCI").Range("B8").Select
Set myRange = Range(Selection, Selection.End(xlToRight))
Debug.Print ("Columns: " & myRange.Columns.Count)
numberOfColumns = (myRange.Columns.Count)
End Function
Sub TieOut(ByVal numberOfRows As Integer, ByVal numberOfColumns As Integer)
Dim i As Integer 'i is row
Dim j As Integer 'j is column
For i = 1 To numberOfRows 'Loop over rows
For j = 1 + 2 To numberOfColumns 'Loop over columns
ThisWorkbook.Worksheets("Loop").Select
With ThisWorkbook.Worksheets("Loop")
**.Cells(i + 3, j + 1).Value = "=INDEX('ZAINET DATA'!$A$1:$H$39038,MATCH(Loop!B$2&TEXT(Loop!$A4,""M/D/YYYY""),'ZAINET DATA'!$C$1:$C$39038,0),4)"
.Cells(i + 3, j + 2).Value = "=INDEX('ZAINET DATA'!$A$1:$H$39038,MATCH(Loop!B$2&TEXT(Loop!$A4,""M/D/YYYY""),'ZAINET DATA'!$C$1:$C$39038,0),5)"**
End With
Next j
Next i
End Sub
Sub Test()
Dim x As Integer
Dim y As Integer
x = numberOfRows()
y = numberOfColumns()
Call TieOut(x, y)
End Sub
Where have you defined it? Is it part of a BAS module?
EDIT: Put Option Explicit as the first line of BAS module & compile (Debug menu -> Compile).
You will see that there are compilation errors.
Remove Dim myRange As Range from Macro1 & Macro2.
Put it at the top of the BAS module (after option explicit)
Note: If you have a variable defined as part of a SUB, other SUB/Functions won't be able to use it. For TieOut to use myRange, it has to be defined at a scope where it can be used by all SUBs.
Also, Macro1 should run first - which assigns the value to MyRange
(i.e. Set MyRange = .....)
If Macro1 is not run, MyRange will hold no value & hence there will be runtime error when your code tries to read the property (MyRange.Rows.Count).
Please take some time to read about Scoping of variables.
A variable needs to hold some value, before you try to read from it.
This is a great example to learn what 'scope' is. You declare (or bring into existence) a variable like the range you're trying to make. It lives inside the macro (or sub procedure) that you made. However, when the sub procedure is finished, your variable no longer has a place to live and gets evicted (dropped out of your computer's memory).
Unfortunately the way your coded your macros will not work the way you are hoping they work. Your myRanges will die everytime they reach an End Sub.
Also when passing arguments (your byvals) to another sub procedure (in this case your TieOut) you must provide the right number of arguments. Your TieOut procedure currently requires two. You cannot pass one and then the other. The correct way would look something like this:
Call TieOut(myRange.Rows.Count, myRange.Columns.Count)
Also you are trying to call a procedure named TieOut2. Not sure if thats a typo, but getting procedure names right is important.
VBA is very powerful and worth learning in my opinion. You look like you are scratching the surface. I would definitely search for some VBA tutorials online. Focus on calling procedures, variable declaration, and scope and I guarantee you will be able to solve your problem :D

Resources