I have an issue with my VBA code. I try to compare 2 columns, both A and B columns. If some data match, for example let's say that A2 contains text in B3, then I need to compare the cell C2 with the column D. I don't understand why but I get the error "End If without block If". Thanks a lot for you help guys.
Here is my code :
Sub Compare()
For i = 1 To 100
For j = 1 To 50
If InStr(1, ActiveSheet.Cells(i, 1).Value, ActiveSheet.Cells(j, 2).Value, vbTextCompare) <> 0 _
Then For k = 1 To 20
If InStr(1, ActiveSheet.Cells(i, 3).Value, ActiveSheet.Cells(k, 4).Value, vbTextCompare) <> 0 Then MsgBox i
End If
Next k
End If
Next j
Next i
End Sub
I found the structure of your if statements a bit confusing and I'm not entirely sure you can do a for loop as a one-liner like that to get rid of all the end ifs. For what it's worth, I think this code is a bit easier to follow:
Sub Compare()
For i = 1 To 100
For j = 1 To 50
If InStr(1, ActiveSheet.Cells(i, 1).Value, ActiveSheet.Cells(j, 2).Value, vbTextCompare) <> 0 Then
For k = 1 To 20
If InStr(1, ActiveSheet.Cells(i, 3).Value, ActiveSheet.Cells(k, 4).Value, vbTextCompare) <> 0 Then MsgBox i
Next k
End If
Next j
Next i
End Sub
This runs w/o a compile error, but can't comment if it does what you want it to do.
sous2817 raised an interesting question in their answer about whether or not a 1-line statement works if the body of the if statement is itself a for-loop. The answer appears to be "no" -- unless the for-loop itself is squeezed onto one line by using the colon statement separator:
Sub test1() 'compile error
Dim i As Long, s As Long
If i = 0 _
Then For i = 1 To 10
s = s + i
Next i
MsgBox s
End Sub
Sub test2() 'compiles okay
Dim i As Long, s As Long
If i = 0 _
Then For i = 1 To 10: s = s + i: Next i
MsgBox s
End Sub
If statements on one line don't need the End If statement.
End If without block If
Sub comparison()
For i = 2 To 1000
For j = 2 To 1000
If Worksheets(Worksheet).Range("A" & i).Value = Worksheets(Worksheet).Range("L" & j).Value Then
Worksheets(worksheet).Range("N" & j).Select
With Selection.Interior
.Pattern = xlSolid
.PatternColorIndex = xlAutomatic
.Color = 65535
.TintAndShade = 0
.PatternTintAndShade = 0
End With
End If
Next j
Next i
End Sub
Related
Ok, so im a very basic user..
Im using the "If" function to find dips in data, when a dip is found column E shows "1", all others are "0". But I need that whole row with the "1" and the next row, even if it has a "0" or "1".
I currently have this:
If ActiveCell.Value = "1" Then
Selection.EntireRow.Cut
Sheets("Sheet2").Select
lMaxRows = Cells(Rows.Count, "A").End(xlUp).Row
Range("A" & lMaxRows + 1).Select
ActiveSheet.Paste
Sheets("Sheet1").Select
Else
So what I need is to tell it to select the row containing "1" (which it already does), as well as the next row.... the rest should cut and append the data to another worksheet.
Great post on alternatives and more reliable methods than ".Select". After reading, you can adjust your code. How to avoid using Select in Excel VBA
To answer your question, replace
Selection.EntireRow.Cut
with
Range(Selection.EntireRow, Selection.Offset(1, 0).EntireRow).Cut
This should get you a good start, you'll need to add some code to not cut all 5 rows above if some of the are blank because they've already been cut or you could remove blank rows on sheet 2 once this code is done.
Sub GetDipsData()
Dim i As Long
Dim c As Long
Dim LastConsecutiveDip As Long
Dim vLastRow As Long
Sheets("Sheet1").Activate
vLastRow = Cells(Rows.Count, "E").End(xlUp).Row
Application.ScreenUpdating = False
For i = 2 To vLastRow
If Cells(i, "E") = 1 Then
s2LastRow = Sheets("Sheet2").Cells(Rows.Count, "E").End(xlUp).Row
For c = i + 1 To vLastRow
If Cells(c, "E") = 1 Then
LastConsecutiveDip = c
Else
Exit For
End If
Next
If c <> i + 2 Then
'copy 5 above and 5 below
If i < 6 Then
Range(Rows(2), Rows(c).Offset(5, 0).EntireRow).Cut Sheets("Sheet2").Range("A" & s2LastRow)
ElseIf c + 5 > vLastRow Then
Range(Rows(i).Offset(-5, 0), Rows(vLastRow).EntireRow).Cut Sheets("Sheet2").Range("A" & s2LastRow)
Else
Range(Rows(i).Offset(-5, 0), Rows(c).Offset(5, 0).EntireRow).Cut Sheets("Sheet2").Range("A" & s2LastRow)
End If
i = c + 5
Else
'just copy 2 rows
If i + 1 > vLastRow Then
Rows(i).Cut Sheets("Sheet2").Range("A" & s2LastRow)
Else
Range(Rows(i), Rows(i).Offset(1, 0).EntireRow).Cut Sheets("Sheet2").Range("A" & s2LastRow)
i = i + 2
End If
End If
End If
Next
Application.ScreenUpdating = True
End Sub
I'm writing a VBA program to search through a large spreadsheet and copy rows that have the same account five or more times associated with the data to a different sheet. The program does exactly what it's supposed to do when I step through each individual line (F8), but when I run the program (F5), it doesn't end up copying any information to the second sheet. I've tried adding a two second delay between switching sheets and pasting the data, just in case this was the problem, but so far it hasn't helped.
Any suggestions?
Edit: I thought that the screen updating might be causing the problem, so I disabled it. The program still didn't paste the data in the other worksheet.
Second Edit: I noticed that when I put a stop in at the beginning of the while loop and step the program through in chunks, it also does not copy and paste the data like it should be. It still works when stepping through individual lines of code, though. I also removed the 2 second pauses as those didn't make a difference.
Here's the code:
Public Sub Main()
Worksheets(2).Activate
Range("A1").Select
Worksheets(1).Activate
Range("C2").Select
AcctName = ActiveCell.Value
LoopControl = 0
AcctNameCt = 1
CurrentAcctRow = ActiveCell.Row
Do While LoopControl <> 1
SecondLoopControl = 0
If AcctName = ActiveCell.Offset(AcctNameCt, 0).Value Then
AcctNameCt = AcctNameCt + 1
If AcctNameCt > 4 Then
GreaterThanFour
End If
ElseIf ActiveCell.Offset(AcctNameCt, 0).Value = "" Then
Exit Do
Else
ActiveCell.Offset(AcctNameCt, 0).Activate
AcctName = ActiveCell.Value
AcctNameCt = 1
CurrentAcctRow = ActiveCell.Row
End If
Loop
End Sub
Public Sub CopyData()
Dim EndRow As Integer
Dim StopCopy As Integer
Dim RestartRow As Integer
EndRow = CurrentAcctRow + AcctNameCt
StopCopy = EndRow - 1
RestartRow = EndRow + 1
ActiveSheet.Range("C" & CurrentAcctRow & ":" & "C" & StopCopy).EntireRow.Copy
Worksheets(2).Activate
LookForEmptyRow
ActiveCell.EntireRow.PasteSpecial
CurrentAcctRow = CurentAcctRow + 1
Worksheets(1).Activate
Range("C" & EndRow).Select
AcctNameCt = 0
End Sub
Public Sub GreaterThanFour()
Do While SecondLoopControl <> 1
If AcctName = ActiveCell.Offset(AcctNameCt, 0).Value Then
AcctNameCt = AcctNameCt + 1
Else
CopyData
SecondLoopControl = 1
End If
Loop
End Sub
Public Sub LookForEmptyRow()
Range("A1").Select
Dim LookAnotherLoopControl As Integer
LookAnotherLoopControl = 0
Do While LookAnotherLoopControl <> 1
If ActiveCell.Value = "" Then Exit Sub Else ActiveCell.Offset(1, 0).Activate
Loop
End Sub
I set the worksheet names to variables and called those, rather than calling the worksheets directly. For some reason, this works better.
Set wbA = Workbooks(Workbook Name)
Set wsA = Worksheets(Worksheet Name 1)
Set wsB = Worksheets(Worksheet Name 2)
Where the "Workbook Name" and "Worksheet Name 1" reflect the actual names, instead. Those are working better than:
Worksheets(2).Activate
LookForEmptyRow
ActiveCell.EntireRow.PasteSpecial
CurrentAcctRow = CurentAcctRow + 1
Worksheets(1).Activate
Range("C" & EndRow).Select
I also used a better method to look for an empty row, rather than writing my own subroutine. The original code had this sub that I wrote:
Public Sub LookForEmptyRow()
Range("A1").Select
Dim LookAnotherLoopControl As Integer
LookAnotherLoopControl = 0
Do While LookAnotherLoopControl <> 1
If ActiveCell.Value = "" Then Exit Sub Else ActiveCell.Offset(1, 0).Activate
Loop
Which, while effective, was highly inefficient. I replaced it with the much more efficient line of code:
lRow = Range("A1000").End(xlUp).Row
Cells(lRow + 1, 1).Activate
Tried to look into cells in a column and then find out if the cells has 6/7 characters and if it has i.
Then copy over.
Sub ctest()
Dim i As Long
For i = 1 To 5000
With Range("AR" & i)
If Left(.Value, 1) <> "i" Then GoTo NextIteration Else
If Len(.Value) = 6 Then .Copy Destination:=.Offset(, 2)
If Len(.Value) = 7 Then .Copy Destination:=.Offset(, 2)
NextIteration:
End With
Next i
End Sub
But this doesn't seem to work well..
Thanks guys.
If you just want to skip the loop, why don't use basic if statement
Sub ctest()
Dim i As Long
For i = 1 To 5000
With Range("AR" & i)
If Left(.Value, 1) = "i" Then
If Len(.Value) = 6 Then .Copy Destination:=.Offset(, 2)
If Len(.Value) = 7 Then .Copy Destination:=.Offset(, 2)
End If
End With
Next i
End Sub
I am trying to set up my user form to do a loop or look up to reference my table which is on a sheet and is a large data base.
I want my user form to look up what I type and then auto fill in the other textboxes so that I can limit the number of duplicates and make it more stream lined.
My code is as shown below is embedded into Textbox1 and is set up to run the code after change. It is still not working and I have worked for many days and weeks trying to figure this out.
Option Explicit
Dim id As String, i As String, j As Integer, flag As Boolean
Sub GetDataA()
If Not IsNumeric(UserForm1.TextBox1.Value) Then
flag = False
i = 0
id = UserForm1.TextBox1.Value
Do While Cells(i + 1, 1).Value <> ""
If Cells(i + 1, 1).Value = id Then
flag = True
For j = 2 To 7
UserForm1.Controls("TextBox" & j).Value = Cells(i + 1, j).Value
Next j
End If
i = i + 1
Loop
If flag = False Then
For j = 5 To 10
UserForm1.Controls("TextBox" & j).Value = ""
Next j
End If
Else
End If
End Sub
you may want to adopt this refactoring of your code
Option Explicit
Sub GetDataA()
Dim j As Integer
Dim f As Range
With UserForm1 '<--| reference your userform
If Not IsNumeric(.TextBox1.Value) Then Exit Sub '<--| exit sub if its TextBox1 value is not a "numeric" one
Set f = Range("A1", Cells(Rows.Count, 1).End(xlUp)).Find(what:=.TextBox1.Value, LookIn:=xlValues, lookat:=xlWhole) '<--| try and find its TextBox1 value along column "A" cells from row 1 down to last not empty one
If f Is Nothing Then '<--| if not found
For j = 5 To 10
.Controls("TextBox" & j).Value = ""
Next j
Else '<--| if found
For j = 2 To 7
.Controls("TextBox" & j).Value = f.Offset(, j - 1).Value
Next j
End If
End With
End Sub
note: if this sub is actually inside UserForm1 code pane than you can change With UserForm1 to With Me
I have a problem with my code, an error appears, and I don;t understand why. The error is:
"Compile error: Next without For"
I do not understand why it is like that. I am new to coding so any help and comments are more than welcome.
This is the code, the Next which is pointed as the one without For is provided a comment.
Sub CGT_Cost()
startrow = Worksheets("GUTS").Cells(10, 1) 'Here I put 1
endrow = Worksheets("GUTS").Cells(11, 1) 'Here I put 1000
For x = endrow To startrow Step -1
If Cells(x, "Q").Value = "Sale" Then
If Cells(x, "D").Value = "1" Then
For i = 1 To 1000
If Cells(x - i, "R").Value <> "1" Then
Next i
Else
Range("G" & x).FormulaR1C1 = "=R[-" & i & "]C/R[-" & i & "]C[-1]*RC[-1]"
End If
End If
End If
Next x
End Sub
Thank you all in advance,
with best regards,
Artur.
Every For statement with a body must have a matching Next, and every If-Then statement with a body must have a matching End If.
Example:
For i = 1 To 10 '<---- This is the header
Hello(i) = "Blah" '<---- This is the body
Next i '<---- This is the closing statement
You have part of the body of your If statement inside your For i loop, and part of it outside. It has to be either ALL inside or ALL outside. Think through the logic and see what it is you want to do.
you have overlapping loops-perhaps
Sub CGT_Cost()
startrow = Worksheets("GUTS").Cells(10, 1) 'Here I put 1
endrow = Worksheets("GUTS").Cells(11, 1) 'Here I put 1000
For x = endrow To startrow Step -1
If Cells(x, "Q").Value = "Sale" Then
If Cells(x, "D").Value = "1" Then
For i = 1 To 1000
If Cells(x - i, "R").Value <> "1" Then
'
Else
Range("G" & x).FormulaR1C1 = "=R[-" & i & "]C/R[-" & i & "]C[-1]*RC[-1]"
End If
Next i
End If
End If
Next x
End Sub