I am trying to detect whether an integer was set, and if not, skip most of the code in the loop (using an if statement). Here is what I have so for.
Do While hws.Cells(r, 9).Value <> ""
On Error Resume Next
ar = Null
ar = aws.Range("A:A").Find(hws.Cells(r, 2).Value).Row
If Not IsNull(ar) Then
'work with ar'
End If
r = r + 1
Loop
However, when I run it, ar = Null has problems. It says "Invalid use of null".
Variables defined as Integer cannot be Null in VBA. You will have to find another way to do what you want. eg use a different data type or use a magic number to indicate null (eg -1).
In your example code, either ar will be assigned a Long value (Range.Row is a Long) or it will throw an error.
just use a variant and isempty:
Dim i
If IsEmpty(i) Then MsgBox "IsEmpty"
i = 10
If IsEmpty(i) Then
MsgBox "IsEmpty"
Else
MsgBox "not Empty"
End If
i = Empty
If IsEmpty(i) Then MsgBox "IsEmpty"
'a kind of Nullable behaviour you only can get with a variant
'do you have integer?
Dim j as Integer
j = 0
If j = 0 Then MsgBox "j is 0"
Find returns a range:
Dim rf As Range
With aws.Range("A:A")
Set rf = .Find(hws.Cells(r, 2).Value)
If Not rf Is Nothing Then
Debug.Print "Found : " & rf.Address
End If
End With
-- http://msdn.microsoft.com/en-us/library/aa195730(office.11).aspx
Related
I'm trying to build a macro, which should find input data in an excel row. Everything works fine when the input data is somewhere available in the excel rows. But when the the input data is not available in the rows, I get a run time error 13 - Type mismatch. I don't understand why this is happening.
My input data, which I declare as a Variant, is something like:
040234-A
0463796
8745-00
These will be found, but if I change the input data to for example 040234- or 04637 I'll get the error.
Does anyone have an idea on how to solve this? In the following you'll find my code. Here "Gescanntes Material" is the important input data. I get the error message at:
MaterialDatenbankZeile = Application.Match(GescanntesMaterial, Range(MaterialienRange), 0)
Thanks!
Sub Traceability()
Worksheets("Materialien").Activate
Dim GescanntesEndgeraet As Variant
Dim GescanntesMaterial As Variant
Dim ZeileEndgeraetStart As Integer
Dim ZeileEndgeraetEnd As Integer
Dim MaterialDatenbankZeile As Integer
Dim MaterialDatenbank As Variant
GescanntesEndgeraet = Worksheets("Uebersicht").Cells(7, "C").Value
GescanntesMaterial = Worksheets("Uebersicht").Cells(11, "C").Value
ZeileEndgeraetStart = Application.WorksheetFunction.Match(GescanntesEndgeraet, Range("A:A"), 0)
ZeileEndgeraetEnd = Application.WorksheetFunction.Match(GescanntesEndgeraet, Range("A:A"), 0) + Application.WorksheetFunction.CountIf(Range("A:A"), GescanntesEndgeraet) - 1
Dim MaterialienRange As String
Let MaterialienRange = "B" & ZeileEndgeraetStart & ":" & "B" & ZeileEndgeraetEnd
MaterialDatenbankZeile = Application.Match(GescanntesMaterial, Range(MaterialienRange), 0)
If IsError(MaterialDatenbankZeile) Then
Let MaterialDatenbank = "A"
Else
MaterialDatenbankZeile = MaterialDatenbankZeile + ZeileEndgeraetStart - 1
MaterialDatenbank = Worksheets("Materialien").Cells(MaterialDatenbankZeile, "B").Value
End If
If MaterialDatenbank = GescanntesMaterial Then
Worksheets("Uebersicht").Cells(7, "F").Value = "true"
Else
Worksheets("Uebersicht").Cells(7, "F").Value = "false"
End If
Worksheets("Uebersicht").Activate
End Sub
Change
Dim MaterialDatenbankZeile As Integer
to
Dim MaterialDatenbankZeile As Variant
otherwise you cannot capture the error value when the Application.Match fails.
FYI pretty much no-one uses Let in VBA - you can omit that.
I have a for loop, and inside it i have if statement.
In my Excel I have a list that contains each value one time. Once I found it i don't want the code to even check the conditional, i want it to skip this part of the if statement completely each time the loop is executed, is it possible?
Here is my code and list:
the first iteration of the loop will find that "c" is the value so it will do what inside it (xc = i)
I don't want the code to even check "ElseIf Cells(1, i) = "c" again, like the following image, is this possible?
code as text:
Sub test()
Dim i, xa, xb, xc As Integer
For i = 1 To 5
If Cells(i, 1) = "a" Then
xa = i
ElseIf Cells(i, 1) = "b" Then
xb = i
ElseIf Cells(i, 1) = "c" Then
xc = i
End If
Next i
End Sub
My initial interpretation of your need was "if the code hits 'c' again, just don't act".
To do so, you could modify the logic as follows:
ElseIf (xc = 0) And (Cells(i, 1) = "c") Then
This way, as soon as xc is set, the first boolean expression would be False, and the overall condition would not ever be met again. As mentioned by #TimWilliams, VBA would still evaluate the second boolean expression, unlike other languages that feature short-circuiting options. #Gene's answer describes a way around this. Typically, for better performance, you would evaluate the simple conditions first, before resorting to costly ones.
Additional notes
In VBA, you must give a type to each variable. In your Dim line, only xc is an Integer, while the other variables are Variants.
An unqualified Cells() call operates on the currently active worksheet, which might not be the expected one. Suggestion: qualify Cells() with the CodeName of your worksheet. The CodeName is what you see or specify under a worksheet's (Name) property as seen from the Visual Basic editor. For example, if (Name) is Sheet1, use Sheet1.Cells(). This will only work if the code resides in the same workbook as Sheet1. If the code is behind the worksheet itself, you can even use Me.Cells().
When dealing with cell values as your code does, VBA is (silently) being nice and understands that, among the numerous properties of the Range class, Value is what you are interested in. It is better practice, however, to explicitly state the target property, such as in Sheet1.Cells(i, j).Value.
EDIT
Knowing the values will be distinct and that there are about 60 of them, I suggest you simply use a Dictionary, as shown below, to get each value's row in one go, without a cascade of Ifs:
Option Explicit
Sub test()
Dim i As Integer
Dim dict As Object 'Scripting.Dictionary
Set dict = CreateObject("Scripting.Dictionary")
For i = 1 To 5
dict(Cells(i, 1).Value) = i
Next
Debug.Print dict("a") '4
Debug.Print dict("b") '2
Debug.Print dict("c") '1
'Etc.
End Sub
if i understood your question you can try this code:
Sub test()
Dim i, xa, xb, xc As Integer
Dim a, b, c As Boolean
a = False
b = False
c = False
For i = 1 To 5
If Cells(i, 1) = "a" And a <> True Then
xa = i
a = True
ElseIf Cells(i, 1) = "b" And b <> True Then
xb = i
b = True
ElseIf Cells(i, 1) = "c" And c <> True Then
xc = 1
c = True
End If
Next i
End Sub
Boolean variable is setted true for example only when the cells(i,1)="a" and after the next "a" value are skipped...
hope this helps
I just wanted to "mod" Ferdinando's code so it's a bit more "readable", I think. The main (the substantive) difference between this version and Ferdinando's or Excelosaurus' is that the cell is not even tested once the value is detected. Remember that the question was: I don't want the code to even check "ElseIf Cells(1, i) = "c" again... So, this version does exactly that.
Sub test()
Dim i As Integer, xa As Integer, xb As Integer, xc As Integer
Dim aFound As Boolean, bFound As Boolean, cFound As Boolean
Dim r As Range
For i = 1 To 5
Set r = Cells(i, 1)
If Not aFound Then
If r = "a" Then xa = i: aFound = True
ElseIf Not bFound Then
If r = "b" Then xb = i: bFound = True
ElseIf Not cFound Then
If r = "c" Then xc = i: cFound = True
End If
Next i
End Sub
I don't like the idea of 60 ElseIfs. Please examine the code below. In order to test it, create a worksheet called "TestSheet" and enter your A1:A5 to cells H2:H6.
Sub TestSpike()
' 06 Jan 2019
Dim Rng As Range
Dim Items As Variant
Dim Spike As String
Dim Tmp As String
Dim i As Integer
Dim R As Long
Items = Split("c|b|0|a|1", "|")
With Worksheets("TestSheet").Columns("H")
For R = 2 To 6
Tmp = CStr(.Cells(R).Value)
If InStr(1, Spike, Tmp, vbTextCompare) = 0 Then
Spike = Spike & "|" & Tmp
On Error Resume Next
i = Application.WorksheetFunction.Match(Tmp, Items, 0)
If Err Then
MsgBox Tmp & " wasn't found in Array"
Else
MsgBox "i = " & i & " = Item " & Tmp
End If
End If
Next R
End With
End Sub
The code has a "Spike". Each item is first checked against the Spike. If it is found there no further tests are carried out. Else, it is added to the Spike.
New items, after being added to the Spike, are checked against the Array "Items" which would hold your 60 elements, separated by Chr(124) thus, Split("c|b|0|a|1", "|"). I use the worksheet function MATCH to look for the item in the array. The result is an index number (or an error, if not found). You can use this index number in a Select Case statement to process each item distinct from others, basically the same way as you now process it when the If statement returns True.
One idea you may find useful with this kind of setup is to use the index from the Match function to return a value from another array. The other array might, for example, contain function names and you use Application.Run to call a different function for each item. This would run significantly faster than examining 60-odd Select Case statements.
I am getting an object defined error with the below code. Any idea what could i be doing wrong? Thanks
Sub Loop_Test2()
Dim i As Integer
Dim j As Integer
Dim CountAll As Integer
Dim CountXL As Integer
ActiveSheet.Range("A1").Activate
CountAll = ActiveSheet.Range("A35")
MsgBox CountAll
For j = 1 To CountAll
i = 1
This is where the error occurs:
CountXL = Cells(i, j).Value
Continued:
MsgBox CountXL
For i = 1 To CountXL + 2
Cells(i + 2, j) = "Row " & i & " Col " & j
Next i
Next j
End Sub
I think it is an incorrect assignment. I'm not familiar with the correct syntax.
Error Details: "Run time error 1004. Application defined or object defined error
Before you edit your question, you was forget to initial your i .So just set the value for i.
In future, you may use Option Explicit at the top of Sub to make sure you declare the variable before using it.
So for your case, just need set i=1, and please declare all the variables as long instead of integer. You may refer here to find out the reason why use long instead of integer.
Good Morning,
I am attempting to create VBA code that will identify if a variable value (number) is found within a string. The string can vary in lenght and can contain 1 or more numbers that are sepearted by a , and a space. I have attempted to use the InStr method but unfortunately if my value is 1 and the string contains a 17 it comes back as true. How can I make it so that would return false since 1 is not equal to 17.
Below is my current code:
'FRB_Code and FRB_Array are variable values within my code but for
'the purpose of this question I have assigned them values.
FRB_Array = "10, 17, 21"
FRB_Code = 1 'ce.Value
If InStr(FRB_Array, FRB_Code) Then
MsgBox "Found"
Else
MsgBox "Not Found"
ce.Delete Shift:=xlUp
End If
Next ce
End If
So the end result should be that the FRB_Code was not found in the FRB_Array and there for the cell was deleted.
Thank you for you help.
You can use an array for that.
Sub FindValue()
Dim sMyString As String
Dim sToFind As String
Dim Arr
Dim i As Long
Dim bFound As Boolean
sMyString = "10, 17, 21"
Arr = Split(sMyString, ", ")
sToFind = "17"
For i = 0 To UBound(Arr)
If Arr(i) = sToFind Then
MsgBox "Found"
bFound = True
Exit For
End If
Next i
If Not bFound Then MsgBox "Not found"
End Sub
Problem is that "1" will "instring" to "1", "217","871", etc. Better to pre-pad and post-pad with spaces:
Sub NumberInString()
BigString = " 1 23 54 79 "
LittleString = " 23 "
MsgBox InStr(1, BigString, LittleString)
End Sub
InStr is not really appropriate here because you are comparing numbers rather than strings. To do what you want split the string into pieces and cycle through the returned array checking each item. Val is used to convert each item in the array to an integer.
bFound = False
FRB_Array = "10, 17, 21"
FRB_Code = 17
ar = Split(FRB_Array, ",")
For i = 0 To UBound(ar)
If FRB_Code = Val(ar(i)) Then
bFound = True
End If
Next i
If bFound Then
MsgBox ("found")
Else
MsgBox ("not found")
End If
You can use REGEX to determine the match.
http://msdn.microsoft.com/en-us/library/twcw2f1c(v=vs.110).aspx
the regex expression would be "1[^\d]|1$" and you would replace 1 with your FB_Code value.
The expression has an or(|) to handle the last number in the array.
I have some vba code to grab information 4 times a minute from a device on it's web configuration page.
I need this to happen from when I place an x in column C and continue until I place an x in column D further down the page.
I have a function I can call which will tell if an X is in the proper place in d, relative to c.
What I'd like to do is have a button which says ok, be ready to scan. then have it start when the first value is entered in c, then stop when the d value is entered.
I'm also having trouble coming up with a way to enter values while the VBA script is actually running.
Any advice? Thanks.
Here is the code to check the columns.
Public Function BackgroundScan(MonitorSpreadsheet As Boolean) As Boolean
Dim LastStart As Integer
Dim LastStop As Integer
intDebug = 1
Select Case MonitorSpreadsheet
Case True
'We are actively testing
If intDebug = 1 Then MsgBox "we ARE monitoring the spreadsheet."
'Call scanning routine here
'Get the status TestingInProgress
LastStart = FindLastStartRow("SVQ")
LastStop = FindLastStopRow("SVQ")
If intDebug = 1 Then MsgBox "LastStart " & LastStart
If intDebug = 1 Then MsgBox "LastStop " & LastStop
Select Case LastStart
Case Is < 20
'We have not started.
If intDebug = 1 Then MsgBox "We have not started."
BackgroundScan = False
'Loop around, and check again
Case Else
'ok we have started, now check to see if we have stopped.
Select Case LastStop
Case Is < LastStart
'**** We ARE testing!!! ****
If intDebug = 1 Then MsgBox "We are testing, and haven't finished."
BackgroundScan = True
Case LastStart
'LastStart and LastStop are the same line, we have started AND finished
If intDebug = 1 Then MsgBox "We have started AND finished!"
BackgroundScan = False
'Loop around, and check again
Case Else
'We have finished testing, and the test spanned multiple rows
BackgroundScan = False
If intDebug = 1 Then MsgBox "We started on one line, and finished on another."
End Select
End Select
Case False
'we are not actively testing
If intDebug = 1 Then MsgBox "We are NOT monitoring the spreadsheet."
BackgroundScan = False
Case Else
MsgBox "Error: Boolean variable reports: " & MonitorSpreadsheet
BackgroundScan = False
End Select
End Function
Here is the code which scans the webpage.
Private Sub CommandButton1_Click()
Dim Some As String 'can't resist a good pun!
Dim intDelay As Integer
Dim intMinDelay As Integer
Dim i As Integer
Dim s As Integer
Dim RunStart As Date
Dim WhichSVBeam As String
Dim lLen As Integer
Dim CurrentSVID As String
Dim CurrentBeamID As String
Dim PreviousSVID As String
Dim PreviousBeamID As String
Dim ColonLocation As Integer
'*******************************************************
'*** Test Continuous Button ***
'*** Where n is specified in cell A6 ***
'*******************************************************
'grab the number of minutes between checking values
intMinDelay = GetValues("A7")
RunStart = Now
'Do this until the end of time, or the execution is halted.
Do 'uncomment do when we are sure the DoEvents will work as we expect
WhichSVBeam = Scan_SVBeam(PreviousSVID, PreviousBeamID)
If InStr(WhichSVBeam, ":") Then
lLen = Len(WhichSVBeam)
ColonLocation = InStr(WhichSVBeam, ":")
'MsgBox WhichSVBeam & ", " & ColonLocation
CurrentSVID = Left(WhichSVBeam, ColonLocation - 1)
'MsgBox CurrentSVID
CurrentBeamID = Right(WhichSVBeam, lLen - ColonLocation)
'MsgBox CurrentBeamID
Else
'no colon, nothing to parse (this shouldn't happen)
MsgBox "No ':' from Scan_SVBeam"
End If
'Call sCheckExecutionTimeGap(RunStart)
'loop for the number of minutes we specified
For i = 1 To intMinDelay
'check every second for events
For s = 1 To 240
Call AppSleep(250)
DoEvents
Next s
Next i
Loop
End Sub
A example of a piece of code that will run at regular intervals, and allows you to change values in your spreadsheet that will be checked, is the following:
Sub testCell()
Dim r1, r2 As Integer
Dim stopIt As Boolean
r1 = doWeStart
r2 = doWeStop(r1)
Debug.Print "The value of cell C1 is now " & [C1].Value
If r1 = 0 Then Debug.Print "We haven't started yet"
If r1 > 0 And r2 = 0 Then Debug.Print "We start but don't stop"
If r1 > 0 And r2 > 0 Then Debug.Print "We started and stopped"
If [C1].Value Like "stop" Or r1 > 0 And r2 > 0 Then stopIt = True Else stopIt = False
If Not stopIt Then
Application.OnTime Now + TimeValue("00:00:05"), "testCell"
End If
End Sub
'
Function doWeStart()
Dim xrow As Integer
' save old selection
Set r = Selection
xrow = 0
' search for "x" in column C
On Error Resume Next
xrow = Application.WorksheetFunction.Match("x", [C:C], 0)
doWeStart = xrow
End Function
'
Function doWeStop(r1)
Dim xrowd As Integer
xrowd = 0
' search for "x" in column D, starting at row r1
On Error Resume Next
xrowd = Application.WorksheetFunction.Match("x", Range("D" & r1, "D1048576"), 0)
If xrowd > 0 Then
doWeStop = xrowd + r1 - 1
Else
doWeStop = 0
End If
End Function
This will run every five seconds, will look for the first "x" in column C and the first "x" in column D below the one found in C. Depending on what is there, it will (for now) print a message in the debug window - you can put your code there. When you enter "stop" in C1, or an "x" is found in both C and D, it stops.
in pseudo code it would be something along th lines of:
start when column c=x
begin loop
get data
check value of column d
if column d= x exit loop
next loop iteration
end
is that what you want?
Philip