I am trying to use VBA to combine data from several different look up ranges for an SQL script writer. At this stage I am trying to identify whether the value I am looking up has an adjacent cell with a specific value. I have tried several iterations. My most recent is the following:
Dim r As String
With Worksheets(12).Range("q2:R80")
r = Cells(.Find(u(0, 1).Row, 18)).Value
End With
I have tried with and without the "With" statement as well as with and without "." before the methods and properties and with and with out the ".Value" and no matter what combination I use, I get an object error of some kind or another. I know I am missing something obvious, but Google has been of no help.
Your parentheses look off:
With Worksheets(12).Range("q2:R80")
r = Cells(.Find(u(0, 1).Row, 18)).Value
End With
seems like it should be:
With Worksheets(12).Range("q2:R80")
r = Cells(.Find(u(0, 1)).Row, 18).Value
End With
But this is still problematic, becauses it assumes the Find succeeded by chaining the .Row call.
Better practice is the following:
With Worksheets(12).Range("q2:R80")
Dim foundRng as Range
Set foundRng = .Find(u(0,1)) '<~ you really should specify the other parameters of Range.Find
If Not foundRng Is Nothing Then
r = Cells(foundRng.Row, 18).Value '<~ make sure to qualify the worksheet the Cells are on
End If
End With
Related
Im not too familiar with VBA, so the code below is mostly just copy and paste and research, just cant couldnt find a way to use Dim multiple times to be able to use it for different columns. Is there something I could add in front of "lastrow" to make it a different without getting the "compile error: Duplication declaration in current scope".
Dim lastrow As Long
lastrow = Worksheets("Sheet1").Range("A" & Rows.Count).End(xlUp).Row
Dim pointer As Integer
pointer = 1
Do Until pointer > lastrow - 1
Range("D1").Select
Sheet1.Range("D" & pointer).NumberFormat = "mm/dd/yyyy"
pointer = pointer + 1
Loop
You can remove the line with .Select in it, as it is not needed, and you can set the .NumberFormat of multiple cells within the loop if you want.
But there is an easier way, you can set the .NumberFormat property of an entire column with one line of code and no loop:
WorkSheets("Sheet1").Range("D:D").NumberFormat = "mm/dd/yyyy"
And for that matter, you can do multiple columns all at once too...
WorkSheets("Sheet1").Range("D:E,G:G,I:J").NumberFormat = "mm/dd/yyyy"
You can't dim a variable multiple times in one function or sub (hence the syntax error). There are some programming languages such as Java which has a notion of block-level scope in which such redeclarations are possible. VBA is not one of those languages.
Perhaps this is an XY problem and what you really want to do is to reinitialize the variable in each pass through an outer loop. That is nonproblematic, just lastrow = 0 whenever you are tempted to redeclare it.
That said, for this particular problem follow the recommendations of braX. In VBA, it is often possible to deal with ranges as a whole rather than looping cell by cell.
I'd think that the following code should produce a diagonal of numbers, and I am wondering why this code doesn't work as intended:
Sub RangeExample()
Dim a As Range
Set a = Sheets(1).Range("a1")
a.Value = 0
Dim i As Integer
For i = 1 To 100
a = a.Offset(1, 1)
a.Value = i
Next i
End Sub
I understand there are many ways of producing a diagonal of numbers, I'm not asking how to do that.
What I'm asking is how I would change my range variable a to become a different range, and do that iteratively. It seems to me that as a.offset(1,1) returns a range object that's one over and one down, I should be able to reassign a as this new range, assign a value, and move on.
Your current issue is that you're missing a Set:
Set a = a.Offset(1, 1)
Note that you could also just use i and not reSet:
a.Offset(i, i).Value = i
Another option is to use Cells, e.g.
Sheets(1).Cells(i + 1, i + 1).Value = i
There's more than one way to skin a cat - pick whatever is easiest and most intuitive to future you.
Thanks for the answer, I didn't know set was required in this case. The specific answer I was looking for I have now found at:
What does the keyword Set actually do in VBA?
Specifically, the following answer by LeppyR64. "Set is used for setting object references, as opposed to assigning a value."
I didn't know that equality alone only impacted the value of the range object a. To actually change the range a was referencing, I needed set because a is supposed to refer to a new range object.
the issue has already been addressed by #BigBen
but you could avoid re-setting the range at every iteration by means of With...End With block
Option Explicit
Sub RangeExample()
Dim i As Long
With Sheets(1).Range("a1") ' reference topleftmost cell
.Value = 0 ' write referenced cell value
For i = 1 To 100
.Offset(i, i).Value = i 'write referenced cell current offset value
Next
End With
End Sub
I have written this code for an OOP in VBA, so I can reuse the same function, while only changing the arguments and calling it in later macros. The point of this code is to create a function with three arguments: to locate the sheet, locate a specific column heading and then what to fill the blanks with. I have limited previous experience with python and java, but cannot manage to get this VBA setup. I think I am very close, but the code needs more tweaks and maybe there are more elegant solutions. The last four lines are for me to see what the variables are as checks.
Sub FillBlanks(sheetname As String, findcolumnn As String, filler As String)
Dim blankcells As Range, columnforchange As Range, abc As Integer
With ThisWorkbook.Worksheets(sheetname).Range("A1:ZZ10000")
Set foundcell = .Find(What:=findcolumn, LookIn:=xlValues, SearchOrder:=xlByColumns, MatchCase:=True)
abc = foundcell.Column
With ThisWorkbook.Worksheets(sheetname)
Set columnforchange = .Range(CStr(abc) & ":" & CStr(abc))
For Each blankcells In columnforchange
If IsEmpty(blankcells.Value) Or blankcells.Value = vbNullString Then
blankcells.Value = filler
End If
Next blankcells
End With
End With
Sheets("Sheet1").Range("A1").Value = abc
Sheets("Sheet1").Range("A2").Value = filler
Sheets("Sheet1").Range("A3").Value = sheetname
Sheets("Sheet1").Range("A4").Value = findcolumn
End Sub
Set columnforchange = .Range(CStr(abc) & ":" & CStr(abc))
will set columnforchange to a row. eg. Range("10:10") --> Row 10
Try
Set columnforchange = .Columns(abc)
With regard to your .Find, try using the After:= argument to specify a starting point. (e.g: After:= .Cells(1,1) Understand that your After:= will not be searched first -- it will be searched last. And, if it is not the first one you want to find, you may need to run your .Find more than once (or use .FindNext)
There are other tweaks that could be made:
Use the Long data type rather than Integer. In VBA, Integer is limited to around 2^15-1, and will internally get converted to a Long anyway.
If your empty cells are truly empty, you might be able to use the .SpecialCells property to find and fill them in one step, instead of looping.
I've run into this a few times, always rather annoyingly. I can work around it, but I'm trying to figure out if there's a reason for it or an easier workaround.
I've defined a range that contains all my data with
Set rngAll = range("A8",cells(usedrange.rows.count,usedrange.columns.count))
Now I want to look through each cell in the first column of that range, and I'm trying to do this:
Set rngUserIDs = rngAll.columns(1)
For each rngCell in rngUserIDs
Do Something
next rngCell
When I run the code, instead of looking at each individual cell in rngUserIDs, rngCell sets itself to match the entirety of rngUserIDs. If rngUserIDs = A8:A1000, instead of starting at A8 and looping down, rngCell immediately = A8:A1000, does its something, then exits the loop.
If I set rngUserIDs any other way, such as:
set rngUserIDs = Range("A8","A" & range("A" & rows.count).end(xlup).row)
For Each rngCell in rngUserIDs
Do Something
next rngCell
it works perfectly, looking at each cell in turn.
Is there a reason for this behaviour? Is there anything I can do about it? Yes, I can work around it, but for various structural reasons it would be convenient if I could just use the first column of rngAll.
You have to specify the Cells property, for reasons I'm not entirely sure I'd have thought the last option was the best though as you want to restrict your range as much as possible.
Set rngUserIDs = rngAll.columns(1)
For each rngCell in rngUserIDs.Cells
Do Something
next rngCell
I am having an issue with runtime error 91, Object variable or With block variable not set. I am trying to write to a cell in a range. Below is my latest code, with various methods I have tried to write to it.
Using .Value didn't work, nor did .Cells(1, 1).Value, nor did Set .Cells(1, 1).Value. Not quite sure what I am missing here.
The value I am trying to write occasionally passes, it seems, but I would like to know exactly what I am doing wrong so I can prevent this from happening in the future.
Thanks,
Code in question, ElseIf is where it hangs up. The If may or may not have problems...
Private Sub IncrementSPN()
If Not tempSPNRange Is Nothing And Not isDuplicate Then
' Increment appropriate SPN family
tempSPNRange.Value = tempSPN + 1
ElseIf tempSPNRange Is Nothing And Not isDuplicate Then
' Don't increment anything, silly!
Set tempSPNRange.Cells(1, 1).Value = tempSPNRange.Cells(1, 1).Value + 1
End If
End Sub
If tempSPNRange has been Dim'ed as a Range, then within the ElseIf it needs to be Set to a group of cells within a worksheet and then values can be assigned to cells within the group.