Loop as if condition - excel

I have the following question.
In the below code i am trying to get my sub to run a loop after the if then statement, and then have the code go back to the first loop and start from the next i. In my below code,everything works fine, but when the condition in the first IF- statement is met, it starts the second loop, but then imidiately exits it again without running it.
So my question is, how do you make a loop after the then statement in a IF- statement?
Sub Sort3()
Dim i As Integer
Dim LastRow As Long
Dim lenght As String
Dim LastRow_2 As String
Dim L_text As Variant
Dim R_text As Variant
Dim M_text As Variant
ThisWorkbook.Sheets("EQ_CLEAN").Select
LastRow = Range("G" & Rows.Count).End(xlUp).Row
LastRow_2 = Range("J" & Rows.Count).End(xlUp).Row
For i = 2 To LastRow
lenght = Range("G" & i)
If Len(lenght) = 25 Then
L_text = Left(Range("A" & i), 12)
R_text = Right(Range("A" & i), 12)
For x = 2 To Last_row_2
On Error Resume Next
n = Worksheets("EQ_CLEAN").Range("D1:D6000").Cells.SpecialCells(xlCellTypeConstants).Count
If L_text <> Sheets("EQ_CLEAN").Range("J" & x) Then
Sheets("EQ_CLEAN").Range(Cells(x, 1), Cells(x, 2)).Select
Application.CutCopyMode = False
Selection.Copy
Range("D" & (n + 1)).Select
Selection.PasteSpecial Paste:=xlPasteFormulas, Operation:=xlNone, _
SkipBlanks:=False, Transpose:=False
End If
Next x
End If
Next i
End Sub

Always use Option Explicit at the beginning. That would have flagged up to you that you dim and set LastRow_2, and then try to use Last_row_2...

Code Review
I don't think your question applies to your problem after careful review of your code. You had many errors present which I've corrected for you below:
Option Explicit
Sub Sort3()
Dim ws As Worksheet: Set ws = ThisWorkbook.Sheets("EQ_CLEAN")
Dim LastRow As Long, LastRow_2 As Long
Dim lenght As String
Dim L_text As String, R_text As String, M_text As String
Dim n As Long, x As Long, i As Long
LastRow = ws.Cells(ws.Rows.Count, "G").End(xlUp).Row
LastRow_2 = ws.Cells(ws.Rows.Count, "J").End(xlUp).Row
For i = 2 To LastRow
lenght = ws.Range("G" & i).Value
If Len(lenght) = 25 Then
L_text = Left(ws.Range("A" & i).Value, 12)
R_text = Right(ws.Range("A" & i).Value, 12)
For x = 2 To LastRow_2
'On Error Resume Next 'You should never use this unless you know exactly which error
'is popping up and why it can't be avoided. Usually this is for 1004 errors that occur
'outside of excel... It's better to use proper error handling instead of skipping all errors.
'You also never tell excel to recognize errors via On Error GoTo 0. I advise you stay away
'from handling errors with these two statements.
'don't make it a habit to assign rogue variables values
n = ws.Range("D1:D6000").Cells.SpecialCells(xlCellTypeConstants).Count
If L_text <> ws.Range("J" & x).Value Then
ws.Range(ws.Cells(x, 1), ws.Cells(x, 2)).Copy
ws.Range("D" & (n + 1)).PasteSpecial Paste:=xlPasteFormulas, Operation:=xlNone, _
SkipBlanks:=False, Transpose:=False
Application.CutCopyMode = xlCopy 'this is the proper format to remove the marching ants
End If
Next x
End If
Next i
End Sub
I tried to put comments where changes were made.
I ran your code on a blank sheet and it doesn't throw any errors. See if it achieves what you're looking for.
Literature To Reference
Error Handling
Avoiding Use of .Select
What Microsoft says about Option Explicit
Why I changed your Row based variables to Long

Related

Calling a macro from within another macro slows execution

This is a follow up on a duration I posted yesterday "Why does this code causes an infinite loop"
Running "SortByEcode" first takes 5.51 seconds
Running "KeepEcode" next takes 1.65 seconds
But if I call "SortByEcode" from within "KeepEcode", KeepEcode takes 4min 54sec to run.
Sub EcodeKeep()
'Needs REH to be sorted first by ECode
TurnOffFunctionality ' Turn off Calculations, DisplayStatusBar, EnableEvents and ScreenUpdating
Dim i As Long
Dim j As Long
Dim LastRow As Long
Dim StartTime As Double
'Sort REH by ECode first
Call SortByEcode 'THIS IS STILL THE BIG PROBLEM
Dim wks As Worksheet
Set wks = rawData5 'Work in sheet("RawEquipHistory")
LastRow = wks.Range("A" & Rows.Count).End(xlUp).Row
StartTime = Timer
Dim Ecodes() As Variant
Ecodes = Application.Transpose(wks.Range("A1:A" & LastRow).Value)
Dim Results As Variant
ReDim Results(UBound(Ecodes) - 1)
wks.Range("AM1") = "ECODE KEEP" 'Add the header to "S1"
For i = LBound(Results) To UBound(Results) - 1
Results(i) = Ecodes(i + 1) <> Ecodes(i + 2)
Next i
'wks.Range("AM2:AM" & i + 1).Value = Results 'I cannot get this line of code to work at all, so added a "write" loop
' outside of maim loop
For j = 0 To i
wks.Range("AM" & j + 2) = Results(j)
Next j
TurnOnFunctionality ' Turn back on at the end
Call EndTimer("EcodeKeep", StartTime)
End Sub
Sub SortByEcode()
TurnOffFunctionality ' Turn off at the start
Dim LastRow As Long
LastRow = ThisWorkbook.Sheets("RawEquipHistory").Range("A" & Rows.Count).End(xlUp).Row
With ThisWorkbook.Sheets("RawEquipHistory").Sort ' SORT sheet by E-Code(a)
.SortFields.Clear
.SortFields.Add Key:=Range("A1:A" & LastRow), Order:=xlAscending
.SetRange Range("A1:AZ" & LastRow)
.Header = xlYes
.Apply
End With
TurnOnFunctionality ' Turn back on at the end
Debug.Print "SortByEcode is Done"
End Sub
Also, I cannot get the line of code provided by Mathieu Guindon #MathieuGuindonto run at all. The code does not kick up any errors but every cell in Range("AM2:AM" & LastRow) equals TRUE. However, if I loop over the Range("AM2:AM" & LastRow)and write the values one at a time I get the correct values.
And if I loop and debug.Pring Results(i) I get the correct results in the immediate window. So I know that the Results array has correct values.
wks.Range("AM2:AM" & i + 1).Value = Results

Excel - How to fill in empty lines below with current value until new value is met in the same column. Non-VBA solution needed [duplicate]

I want to fill in all empty cells using values of above cells
state name
IL Mike
Sam
CA Kate
Bill
Leah
Should be as follows
state name
IL Mike
IL Sam
CA Kate
CA Bill
CA Leah
I tried the following
Sub split()
Dim columnValues As Range, i As Long
Set columnValues = Selection.Area
Set i = 1
For i = 1 To columnValues.Rows.Count
If (columnValues(i) = "") Then
columnValues(i) = columnValues(i - 1)
End If
Next
End Sub
I get an error when I set i. How can I modify my code
For those not requiring VBA for this, select ColumnA, Go To Special..., Blanks and:
Equals (=), Up (▲), Ctrl+Enter
should give the same result.
Given you asked for VBA, there is a quicker way than looping (the VBA equivalent of what pnuts posed above, with the additional step of removing the formula at the end):
On Error Resume Next
With Selection.SpecialCells(xlCellTypeBlanks)
.FormulaR1C1 = "=R[-1]C"
.Value = .Value
End With
It is because i should be defined as i=1. There are although a few other problems with the code. I would change it to something like this:
Sub split()
Dim columnValues As Range, i As Long
Set columnValues = Selection
For i = 1 To columnValues.Rows.Count
If columnValues.Cells(i, 1).Value = "" Then
columnValues.Cells(i, 1).Value = columnValues.Cells(i - 1, 1).Value
End If
Next
End Sub
Sub fill_blanks()
Dim i As Long
i = 2 ' i<>1 because your first raw has headings "state " "name"
'Assume state is in your cell A and name is in your cell B
Do Until Range("B" & i) = ""
Range("B" & i).Select
If ActiveCell.FormulaR1C1 <> "" Then
Range("A" & i).Select
If ActiveCell.FormulaR1C1 = "" Then
Range("A" & i - 1).Copy
Range("A" & i).PasteSpecial Paste:=xlPasteValues
Else
i = i + 1
End If
Else
i = i + 1
End If
Loop
End Sub
For some cause the method used on post https://stackoverflow.com/a/20439428/2684623 not work for me. When the line .value=.value is executed, I get the error 'not available' (#N/D for local language) in the value of cells. Version of Office is 365.
I dont know the reason however with some modifications runs fine:
Sub TLD_FillinBlanks()
On Error Resume Next
With ActiveSheet.UsedRange.Columns(1)
If .Rows(1) = "" Then .Rows(1).Value = "'"
.SpecialCells(xlCellTypeBlanks).FormulaR1C1 = "=R[-1]C"
.Value = .Value
End With
End Sub
Using loops:
Sub TLD_FillinBlanksLoop()
Dim rCell As Range
For Each rCell In ActiveSheet.UsedRange.Columns(1).Cells
If rCell.Value = "" And rCell.Row > 1 Then
rCell.FillDown
End If
Next
End Sub
I hope that can be useful for somebody. Thanks and regards.
Here is the whole module, I pasted the formulas as values at the end.
Sub FillBlanksValueAbove()
Dim sName As String
sName = ActiveSheet.Name
Dim ws As Worksheet
Dim lastRow As Long, lastCol As Long
Dim rng As Range
'Set variable ws Active Sheet name
Set ws = Sheets(sName)
With ws
'Get the last row and last column
lastRow = .Range("A" & .Rows.Count).End(xlUp).Row
lastCol = .Cells(1, .Columns.Count).End(xlToLeft).Column
'Set the range
Set rng = .Range(.Cells(1, 1), .Cells(lastRow, lastCol))
rng.Select
'Select Blanks
rng.SpecialCells(xlCellTypeBlanks).Select
'Fill Blanks with value above
Selection.FormulaR1C1 = "=R[-1]C"
'Paste Formulas as Values
rng.Select
Selection.Copy
Selection.PasteSpecial Paste:=xlPasteValuesAndNumberFormats, Operation:= _
xlNone, SkipBlanks:=False, Transpose:=False
End With
End Sub

How do I get away from Select and Copy and write better code?

Can you explain how I can get away from using select and copy in this code? I want to make it run as efficiently as possible and without screen updating. I know I can set the screenupdating = false, but i prefer to just have the code written better!
Dim i As Integer
For i = 4 To 501
Sheets("Repository").Range("B" & i).Copy
Sheets("Input").Activate
Sheets("Input").Range("M13").Select
Selection.PasteSpecial Paste:=xlPasteValues
Sheets("Input").Range("M21").Copy
Sheets("Repository").Activate
Sheets("Repository").Range("E" & i).Select
Selection.PasteSpecial Paste:=xlPasteValues
Sheets("Input").Range("U12").Copy
Sheets("Repository").Activate
Sheets("Repository").Range("C" & i).Select
Selection.PasteSpecial Paste:=xlPasteValues
Sheets("Input").Range("V12").Copy
Sheets("Repository").Activate
Sheets("Repository").Range("D" & i).Select
Selection.PasteSpecial Paste:=xlPasteValues
Next i
Thanks so much.
If you're only moving values from one cell to another, there's no need to copy/paste. If you have to copy a lot of formatting over then there may be a need for it. This should accomplish the same thing, in my view it's the simplest way to go about it--
Dim wsRepository as Worksheet
Set wsRepository = ThisWorkbook.Sheets("Repository")
Dim wsInput as Worksheet
Set wsInput = ThisWorkbook.Sheets("Input")
Dim i As Integer
For i = 4 To 501
wsInput.Range("M13") = wsRepository.Range("B" & i)
wsRepository.Range("E" & i) = wsInput.Range("M21")
wsRepository.Range("C" & i) = wsInput.Range("U12")
wsRepository.Range("D" & i) = wsInput.Range("V12")
Next i
You can eliminate a lot of the activating and selecting. Here's how I would write it:
Application.ScreenUpdating = False
For i = 4 To 501
Sheets("Repository").Range("B" & i).Copy
Sheets("Input").Range("M13").PasteSpecial Paste:=xlPasteValues
Sheets("Input").Range("M21").Copy
Sheets("Repository").Range("E" & i).PasteSpecial Paste:=xlPasteValues
Sheets("Input").Range("U12").Copy
Sheets("Repository").Range("C" & i).PasteSpecial Paste:=xlPasteValues
Sheets("Input").Range("V12").Copy
Sheets("Repository").Range("D" & i).PasteSpecial Paste:=xlPasteValues
Next i
Application.ScreenUpdating = True
I would still recommend setting screenupdate to false. It will run a lot faster if it doesn't need to show the user each action it's taking.
First of all you don't need to select/activate/copy... you can simply assign values from one cell to another (with/without using variables). I would do this:
Sub test()
Dim i As Long 'Integer has a strict limit
Dim j As Integer
Dim RepositoryWs As Worksheet
Dim InputWs As Worksheet
Dim destinationCell(1 To 4) As Range
Dim sourceCell(1 To 4) As Range
Set RepositoryWs = Worksheets("Repository")
Set InputWs = Worksheets("Input")
'Static ranges
With InputWs
Set destinationCell(1) = .Range("M13")
Set sourceCell(2) = .Range("M21")
Set sourceCell(3) = .Range("U12")
Set sourceCell(4) = .Range("V12")
End With
For i = 4 To RepositoryWs.Range("B4").End(xlDown).Row 'Not hardcoded -> it works if you'll have more data on Repository sheet
'Dynamic ranges
With RepositoryWs
Set sourceCell(1) = .Range("B" & i)
Set destinationCell(2) = .Range("E" & i)
Set destinationCell(3) = .Range("C" & i)
Set destinationCell(4) = .Range("D" & i)
End With
For j = 1 To 4
destinationCell(j).Value = sourceCell(j).Value
Next j
Next i
End Sub

Filling any empty cells with the value above

I want to fill in all empty cells using values of above cells
state name
IL Mike
Sam
CA Kate
Bill
Leah
Should be as follows
state name
IL Mike
IL Sam
CA Kate
CA Bill
CA Leah
I tried the following
Sub split()
Dim columnValues As Range, i As Long
Set columnValues = Selection.Area
Set i = 1
For i = 1 To columnValues.Rows.Count
If (columnValues(i) = "") Then
columnValues(i) = columnValues(i - 1)
End If
Next
End Sub
I get an error when I set i. How can I modify my code
For those not requiring VBA for this, select ColumnA, Go To Special..., Blanks and:
Equals (=), Up (▲), Ctrl+Enter
should give the same result.
Given you asked for VBA, there is a quicker way than looping (the VBA equivalent of what pnuts posed above, with the additional step of removing the formula at the end):
On Error Resume Next
With Selection.SpecialCells(xlCellTypeBlanks)
.FormulaR1C1 = "=R[-1]C"
.Value = .Value
End With
It is because i should be defined as i=1. There are although a few other problems with the code. I would change it to something like this:
Sub split()
Dim columnValues As Range, i As Long
Set columnValues = Selection
For i = 1 To columnValues.Rows.Count
If columnValues.Cells(i, 1).Value = "" Then
columnValues.Cells(i, 1).Value = columnValues.Cells(i - 1, 1).Value
End If
Next
End Sub
Sub fill_blanks()
Dim i As Long
i = 2 ' i<>1 because your first raw has headings "state " "name"
'Assume state is in your cell A and name is in your cell B
Do Until Range("B" & i) = ""
Range("B" & i).Select
If ActiveCell.FormulaR1C1 <> "" Then
Range("A" & i).Select
If ActiveCell.FormulaR1C1 = "" Then
Range("A" & i - 1).Copy
Range("A" & i).PasteSpecial Paste:=xlPasteValues
Else
i = i + 1
End If
Else
i = i + 1
End If
Loop
End Sub
For some cause the method used on post https://stackoverflow.com/a/20439428/2684623 not work for me. When the line .value=.value is executed, I get the error 'not available' (#N/D for local language) in the value of cells. Version of Office is 365.
I dont know the reason however with some modifications runs fine:
Sub TLD_FillinBlanks()
On Error Resume Next
With ActiveSheet.UsedRange.Columns(1)
If .Rows(1) = "" Then .Rows(1).Value = "'"
.SpecialCells(xlCellTypeBlanks).FormulaR1C1 = "=R[-1]C"
.Value = .Value
End With
End Sub
Using loops:
Sub TLD_FillinBlanksLoop()
Dim rCell As Range
For Each rCell In ActiveSheet.UsedRange.Columns(1).Cells
If rCell.Value = "" And rCell.Row > 1 Then
rCell.FillDown
End If
Next
End Sub
I hope that can be useful for somebody. Thanks and regards.
Here is the whole module, I pasted the formulas as values at the end.
Sub FillBlanksValueAbove()
Dim sName As String
sName = ActiveSheet.Name
Dim ws As Worksheet
Dim lastRow As Long, lastCol As Long
Dim rng As Range
'Set variable ws Active Sheet name
Set ws = Sheets(sName)
With ws
'Get the last row and last column
lastRow = .Range("A" & .Rows.Count).End(xlUp).Row
lastCol = .Cells(1, .Columns.Count).End(xlToLeft).Column
'Set the range
Set rng = .Range(.Cells(1, 1), .Cells(lastRow, lastCol))
rng.Select
'Select Blanks
rng.SpecialCells(xlCellTypeBlanks).Select
'Fill Blanks with value above
Selection.FormulaR1C1 = "=R[-1]C"
'Paste Formulas as Values
rng.Select
Selection.Copy
Selection.PasteSpecial Paste:=xlPasteValuesAndNumberFormats, Operation:= _
xlNone, SkipBlanks:=False, Transpose:=False
End With
End Sub

Taking a Reference cell, searching through 2nd sheet, replace data with same identifier

I decided to change my tact.
I decided to take another shot at this, but in a new way. I did a weekend long Google marathon and found I believe my answer,
Option Explicit
Sub DataUpdate()
Dim rFind As Long, NR As Long, LR As Long, LC As Long
LR = Range("C" & Rows.Count).End(xlUp).Row
LC = Cells(2, Columns.Count).End(xlToLeft).Column
NR = LR + 1
On Error Resume Next
rFind = Range("A25:A" & LR).Find(Range("A1")).Row
On Error GoTo 0
If rFind = 0 Then
If MsgBox("Customer record not found, add to dataset?", vbYesNo + vbQuestion) = vbYes Then
Range("A2", Cells(LC, 2)).Copy
Range("C" & NR).PasteSpecial xlPasteValues
Range("A1", Cells(1, LC)).ClearContents
Exit Sub
End If
Else
Range("A2", Cells(2, LC)).Copy
Range("A" & rFind).PasteSpecial xlPasteValues
Range("A1", Cells(1, LC)).ClearContents
End If
End Sub
Looking at this I just want a cleaner explanation instead of just taking it as is, and using it without knowing what I am doing.
Here is the sheet it is on:
http://dl.dropbox.com/u/3327208/Excel/Replace.zip
If I add this to my code, regurgitate this code I see I can do this, I just want to verify that this is correct.
Option Explicit
Sub PENCMR()
Dim i As Integer
With Application
.ScreenUpdating = False
End With
'Internal NCMR
Dim wsPE As Worksheet
Dim wsNDA As Worksheet
'Copy Ranges
Dim c As Variant
'Paste Ranges
Dim p As Range
'Setting Sheet
Set wsPE = Sheets("Print-Edit NCMR")
Set wsNDA = Sheets("NCMR Data")
Set p = wsPE.Range("A54:U54")
With wsPE
c = Array(.Range("AG2"), .Range("B11"), .Range("B14"), .Range("B17"), .Range("B20"), .Range("B23") _
, .Range("Q11"), .Range("Q14"), .Range("Q17"), .Range("Q20"), .Range("R25"), .Range("V23") _
, .Range("V25"), .Range("V27"), .Range("B32"), .Range("B36"), .Range("B40"), .Range("B44") _
, .Range("D49"), .Range("L49"), .Range("V49"))
End With
For i = LBound(c) To UBound(c)
p(i + 1).Value = c(i).Value
Next
With wsNDA
Dim rFind As Long, NR As Long, LR As Long, LC As Long
LR = Range("C" & Rows.Count).End(xlUp).Row
LC = Cells(2, Columns.Count).End(xlToLeft).Column
NR = LR + 1
rFind = wsNDA.Range("A:A" & LR).Find(Range("A54")).Row
Range("A54", Cells(2, LC)).Copy
Range("A" & rFind).PasteSpecial xlPasteValues
Range("A54", Cells(1, LC)).ClearContents
End With
With Application
.ScreenUpdating = True
End With
End Sub
The code runs, but it doesn't come back with an error, yet it doesn't run completely. It hits to the point where it drags everything down, then it seems to die there. Can someone help me find out why it doesn't do what I think it should do, which is copy the row, search for the number in column A, and then write over it with the correct data in row 54...
I know something is wrong, but I don't have the skills to figure out what, if someone can help me it be greatly appreciated.
I am not 100% sure of what you are trying to achieve but there are several problems in your code:
Instead of
Set p = wsPE.Range("A54:U54")
For i = LBound(c) To UBound(c)
p(i + 1).Value = c(i).Value
Next
You probably mean
Set p = wsPE.Range("A54")
For i = LBound(c) To UBound(c)
p.Offset(0, i) = c(i)
Next
In your With wsNDA block, you need to put . before the Range and Cells, for example:
.Range("A54", .Cells(2, LC)).Copy
Finally:
I would remove the ScreenUpdating statements for now, and run the code in debug mode (F8) to see step by step what the code is doing and check the values of your variables if necessary use "Add Watch"
I would avoid using a range to store temporary data. You could use a 2D array instead, like this for example:
Dim data As Variant
Redim data(1 To 1, 1 To 21) As Variant
for i = xx To yy
data(1,i+1) = c(i)
Next i
yourTargetCell.Resize(1, UBound(data,2)) = data

Resources