I have a part of a macro that is being completely skipped when it is executing. I think I probably has something to do with the if statement combined with the loop.
For x = 1 To HomeLoop
If Application.WorksheetFunction.CountIf(Sheet6.Range("G:G"), Sheets(1).Cells(x + 2, 1)) = 0 Then
Sheets(1).Select
Rows(x + 2).Select
Selection.Delete Shift:=xlUp
Else
End If
Next x
There is more code before and after the block. When I'm going through the code with the debugger, the "For x = 1 to HomeLoop" will highlight and the next step goes directly to highlight the code below the "Next x" I can't figure out why the block is being completely skipped.
Any help is greatly appreciated.
Thank you.
The for loop basically runs an if statement each time it runs through
for x = 1 to homeloop is basically equivalent to if x<=homeloop
my guess is homeloop doesn't have a value in it or is less then 1. Do you have any sort of error handling in the code?
Related
Hi all I have the strangest type mismatch in my VB Script. I have no idea what to do.
I define my global variables up top.
Preamble:
Public intX, intY, firstX,firstY
intX = CInt(InputBox("Set X Coordinate above ONE in Excel to Start pasting"))
intY = CInt(InputBox("Set Y Coordinate above ONE in Excel to Start pasting"))
firstX = intX
firstY = intY
firstY = CInt(firstY)
I then have some code running here (note:firstY and firstX go untouched, they are treated as anchors, in a way). Then I have the below loop which adds rows based on the count of existing rows (intY becoming the delimiter for the loop) and certain arguments. firstY exists to help this loop retrace to the starting position of the excel sheet so it can work its way down.
Dim x, y, adder, i
adder=1
i=0
x=0
y=0
Do While x<intY
If ws.Cells(firstY, IntX+3)+ws.Cells(firstY,IntX+5)>1 Then
adder=ws.Cells(firstY, IntX+3)+ws.Cells(firstY,IntX+5)-1
For i = 1 to adder
ws.Cells(firstY+1,IntX).EntireRow.Insert
Next
End If
firstY=firstY+adder+1
adder=0
x=x+1
Loop
I get the error Type mismatch: '[string: ""]' on line If ws.Cells(firstY, IntX+3)+ws.Cells... Then
Any ideas as to why this is occuring?
When I print the types they actually look OK (all int, but eventually firstY becomes type double as the loop keeps going)
' MsgBox TypeName(firstY) &" " & TypeName(x) &" " & TypeName(intY)
I'm sorry if I come off like a coding cretin but I really don't know what to do here.
Any help in troubleshooting is appreciated.
There are a lot of questions regarding this issue. I read many of them and tried a few things but they don't fix my case.
I am trying to compare lines from two different (very long) sheets. If specific indices match then specific cells (always the same columns with the current line) need to be copied from one sheet into the other.
It looks like this just bigger (enlarged example):
Dim ArrayOne() as string
Dim ArrayTwo() as string
Redim ArrayOne (1 to AmountOfRowsSheet1)
Redim ArrayTwo (1 to AmountOfRowsSheet2)
For i = 1 to AmountOfRowsSheet1
ArrayOne(i) = Sheet1.Cells(i, ThisColumn)
next i
For i = 1 to AmountOfRowsSheet2
ArrayTwo(i) = Sheet2.Cells(i, ThatColumn)
next i
for i = 1 to 4600
for j = 1 to 69000
if ArrayOne(i) Like "*" & ArrayTwo(j) then
Sheet1.Cells(i, 5).value = Sheet3.Cells(i,10).value
'the line above is repeated about 20 times just with different columns
'so it gets potentially executed 4600*69000*20 times (6348000000)
end if
next j
next i
For-loop and everything is working, it also copies correctly but after an amount of lines I run out of memory. In the TaskManager I can see my used RAM tick up every few seconds. At one point Excel displays an error that it can't handle the next copying because of a lack of resources.
I tried:
Application.CutCopyMode = False '( at restart of loop)
Creating an empty data object and putting it into the clipboard.
and a few user32.dll fixes I found.
I turned your example into how you would work with arrays
Option Explicit
Sub Example()
Dim ArrayOne() As Variant
Dim ArrayTwo() As Variant
ArrayOne = Sheet1.Columns(1).Value 'read column 1 into array
ArrayTwo = Sheet2.Columns(2).Value 'read column 2 into array
Dim start
start = Timer
Dim i As Long
For i = 1 To 4600
Dim j As Long
For j = 1 To 69000
If ArrayOne(i, 1) Like "*" & ArrayTwo(j, 1) Then
Sheet.Cells(i, 5).Value = Sheet.Cells(i, 10).Value + 1
End If
Next j
Debug.Print i, start, Timer, "Runtime=" & Timer-start
Stop 'we want to test time of one iteration = 23 seconds
Next i
End Sub
This example run 23 seconds (on my computer) for one iteration of the j loop. So this will run in total 23*4600 seconds which is about 30 hours.
So either you strip down the data that needs to be processed or you use something else than Excel VBA to get it faster. Or you change your entire approach.
Note that VBA is limited to single threading. So no matter how many cores your CPU has VBA will only use one. That makes it actually a pretty bad tool for processing big data.
Actually what you need to get rid of is the read/write actions to the cells
Sheet.Cells(i, 5).Value = Sheet.Cells(i, 10).Value
Whenever you access a cell value it slows down a lot. Without that line the loop runs in 2 instead of 23 seconds (still a total runtime of 2.5 hours). So there is potential to get this faster, but probably not much faster than 2.5 hours.
If you cannot get rid of multiple read/write actions then even turning off calculation Application.Calculation = xlCalculationManual before going into the loop brings an immense boost. Just don't forget to turn it on Application.Calculation = xlCalculationAutomatic in the end. Note that turning off calculation only works if you have no formulas that need to be calculated while your loop runs (otherwise you get faulty results).
I recommend to try to improve your real code like above and check the runtime for one full run of the inner j loop as I did with the stop command. This way you can easily calculate the entire runtime by multiplication with 4600.
Not an answer to the question but instead of:
For i = 1 to AmountOfRowsSheet1
ArrayOne(i) = Sheet1.Cells(i, ThisColumn)
next i
try:
ArrayOne= Range(Cells(1, ThisColumn), Cells(AmountOfRowsSheet1, ThisColumn))
ArrayOne will be a 2D array, with data starting in (1,1) and incrementing (n,1)...
Quicker to get data and similar can be used for putting an array back into a worksheet - also miles quicker than a for loop.
Edit: Again, not direct answer to the question, but this:
import random, string
# ---------------------------------------------------------
#This part is just generating random data to compare against each other (and in case of lists 5 & 6, the data on the sheet in Sheets1(i,5) & Sheets3(i,10)
N1 = 6
list2 = []
list5 = [] #This would correspond to existing vals in Sheets(1.cells(i,5)
list6 = [] #and this to Sheets3.cells(i,10)
for i1 in range(0, 4600):
list2.append(''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(N1)))
list5.append(5)
list6.append(10)
N2 = 12
list3 = []
for i1 in range(0, 69000):
list3.append(''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(N2)))
list2[0] = "$$$$$$" #Just setting two values so we can check the method works
list3[10] = "$$$$££££££"
# ---------------------------------------------------------
#This part is actually doing what your trying to do in VBA
list4 = []
ij1 = 0
for j1 in list2:
found = False
for j2 in list3:
if j1 in j2:
found = True
break
if found:
list4.append(list6[j1])
else:
list4.append(list5[j1])
ij1 += 1
The bit your interested in runs in around 25 seconds. Absolutely no fancy code-work needed. Go look at downloading anaconda. You'd probably be quicker reading your two excel files into python, do your ops, then writing back out again than trying to do it purely in VBA.
i have a code that copies and rewrites anything thats between "(" and ")", but now i have different type of data which do not end with ")" so, i need it to stop when it reaches the last character in cell. Maybe it is dumb question but i cant seem to find how to fix my problem. I am a student and total newbie in vba (5 days ago i didn't know what vba is...) also sorry for my bad english.
I've tried to search (in here, google, youtube) but i couldnt find anything i need
'zaciatok=start koniec=end dlzka=length
Do While Mid(LookInHere, y, 1) <> ""
If Mid(LookInHere, Z, 1) = "(" Then
zaciatok = Z
End If
If Mid(LookInHere, y, 1) = ")" Then
koniec = y
dlzka = (koniec - 1) - zaciatok
dlzka = Abs(dlzka)
SplitCatcher = Mid(LookInHere, zaciatok + 1, CStr(dlzka))
MsgBox SplitCatcher
End If
y = y + 1
Z = Z + 1
Loop
In your specific implementation, one option is to modify your Do While ... loop to also test against the length of the string. That line would look something like:
Do While Mid(LookInHere, y, 1) <> "" And y < Len(LookInHere)
That modification tells the statement that it should terminate the loop when the iterating variable y goes past the length of the statement.
Another option is to change it from a Do While loop to a For loop. It would read something like:
For i = 1 to Len(LookInHere)
MsgBox Mid(LookInHere, i, 1)
'Input your logic here
Next i
The problem is that each of these versions is relatively inefficient, looping through each letter in a string a performing a calculation. Consider using built-in Excel functions. The Instr returns the position of a character, or a zero if it is not found. As an example, Instr("Abcdef", "b") would return the number 2, and Instr("Abcdef", "k") would return zero. You can replace the entire loop with these two function calls.
Z = Instr(LookInHere, "(")
y = Instr(LookInHere, ")")
If y = 0 Then y = Len(LookInHere)
Final note: if your patterns begin to get more and more complex, consider reviewing and implementing regular expressions.
You can use Right(LookInHere, 1) to get the last character of LookInHere
The following code is exhibiting the following bizarre behavior:
1.) if I set the step to zero it moves from cell to cell just fine, the message box counts out 1 through 8 (For i = 1 to 8 Step 0).
2.) if I set the step to one it gives the sequence 1, 3, 5, 7 (For i = 1 to 8 Step 1). It is my understanding that Step 1 should be producing 1, 2, 3, 4, 5, 6, 7, 8 for the message box return.
Private Sub CommandButton1_Click()
Dim i As Integer
For i = 1 To 8 Step 1
MsgBox i
ActiveCell.Offset(1, 0).Range("A1").Select
i = i + 1
Next i
End Sub
The math of this makes sense of course, but the mechanics according to standard excel looping seems bizarre because this yields the same result as (with no step increment specified):
Private Sub CommandButton1_Click()
Dim i As Integer
For i = 1 To 8
MsgBox i
ActiveCell.Offset(1, 0).Range("A1").Select
i = i + 1
Next i
End Sub
that is the MsgBox sequence is 1, 3, 5, 7. The point is that I know appending "Step 0" to the for statement gives the unitary increments but this feels like a work-around for leaving off the Step increment all together? I have to wonder if my 2013 excel pro plus is corrupted. Please clue me in as to whether this is normal or not. TIA.
But you have this line in the code:
i = i + 1
Which makes it jump double because it's your loop variable too!
So For increments the variable for you, you don't have to do i = i + 1 in the loop. You would have to, if it was a while loop. But never in a For loop, because you're interfering with the loop. (Unless this is your exact intention.)
Folks what can I say? Well a little more actually, if you look at the block of code that I didn't include:
Private Sub CommandButton1_Click()
Dim i As Integer
For i = 1 To 8 Step 0
MsgBox i
ActiveCell.Offset(1, 0).Range("A1").Select
i = i + 1
Next i
End Sub
you might be able to appreciate the value of the "Step 0", this allows the user total control over the incremental step via i = i +1, or more generally for a function f(), i = f(i). Hence not incrementing at the "For i = ..." level may actually have some utility depending on what kind of jam one find's oneself in.
In summary: a.) leaving out the Step modifier is equivalent to the default i.e., Step = 1. b.) zeroing the Step modifier, Step = 0 requires a "do while" approach with explicit incrementing via i = f(i) placed within the loop. Finally, and I'm not going to test this, but with Step = 0 and no increasing incrementing may cause an infinite loop or throw an error depending on the situation (so it is probably a good idea to avoid Step = 0 unless you are sure about your f(i) and it's placement within the loop block!).
I need the macro to loop if one of the criteria is not met. Working with some basic scraping. I need this to loop until ready before I start gathering data.
If ie.document.getElementsByClassName("clicks").Length < 1 Or ie.document.getElementsByClassName("feedback").Length < 1 Then
Do: Loop
End If
I think it would look like this :
Do While ie.document.getElementsByClassName("clicks").Length >= 1 And ie.document.getElementsByClassName("feedback").Length >= 1
'''code
Loop
Your loop does not check with each iteration, only once and if it is not ready then it will enter an infinite loop. You need a solution that checks each time the loop iterates.
Do
If ie.document.getElementsByClassName("clicks").Length < 1 Or ie.document.getElementsByClassName("feedback").Length < 1 Then
DoEvents 'free the processor to work on other things
Else
Exit Do
End If
Loop
This will check each time the loop iterates rather than just once.