VBA If with two criteria in two columns - vba

I need to look at two cells (C and F) on a each row, and if the value for C to ends with 30 and the value for F is greater than Zero, copy and paste the row to another sheet. I've managed to get the copy and paste to work using 1 criteria, but I cannot figure out how to get both criteria to work together.
Sub compile1()
Dim x As String
Set rSearch = Sheets("Application").Range("C:C")
For Each cell In rSearch
x = cell.Value
If Right(cell, 2) = "30" And cell.Offset(, 3) > 0 Then
matchRow = cell.Row
Rows(matchRow & ":" & matchRow).Select
Selection.Copy
Sheets("sheet2").Select
ActiveSheet.Rows(matchRow).Select
ActiveSheet.Paste
Sheets("Application").Select
End If
Next
End Sub

Here you go:
Sub CP()
Dim i As Long
Dim n As Long
n = Sheets("Application").Cells(Rows.Count, 3).End(xlUp).Row
For i = 1 To n
With Sheets("Application")
If Right(Cells(i, 3), 2) = 30 And Cells(i, 6).Value > 0 Then
.Cells(i, 3).EntireRow.Copy Destination:=Sheets("Sheet3").Cells(i, 3)
.Cells(i, 6).EntireRow.Copy Destination:=Sheets("Sheet3").Cells(i, 6)
End If
End With
Next i
End Sub
I have used column 3 to count the number of rows and hence assumed this is the main column

You were missing the Next statement in your second for each loop.
The two criterias can be taken together with this line:
If y > 0 And Right(x, 2) = "30" Then
so the whole code would be...
Sub compile1()
Dim x As String
Dim y As Integer
Dim rSearch As Range
Dim rSearch1 As Range
Dim cell As Range, cell1 As Range
Dim matchRow As Integer
Set rSearch = Sheets("Application").Range("C:c")
Set rSearch1 = Sheets("Application").Range("F:F")
For Each cell In rSearch
x = cell.Value
For Each cell1 In rSearch1
y = cell1.Value
If y > 0 And Right(x, 2) = "30" Then
matchRow = cell.Row
Rows(matchRow & ":" & matchRow).Select
Selection.Copy
Sheets("sheet2").Select
ActiveSheet.Rows(matchRow).Select
ActiveSheet.Paste
Sheets("Application").Select
End If
Next cell1
Next cell
End Sub

To speed things up I'd suggest the following:
Sub Copy_Paste()
Dim x As String
Dim y As Integer
Dim WS1 As Worksheet
Set WS1 = ActiveSheet
y = 1
Do Until y > WorksheetFunction.Max(Range("C1048576").End(xlUp).Row, Range("F1048576").End(xlUp).Row)
x = Trim(Cells(y, 3).Value)
If Right(x, 2) = "30" And (IsNumeric(Cells(y, 6).Value) And Cells(y, 6).Value > 0) Then Rows(y & ":" & y).Copy: Sheets("Sheet2").Range("A" & Sheets("Sheet2").Range("C1048576").End(xlUp).Row + 1).PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks:=False, Transpose:=False: Application.CutCopyMode = False
y = y + 1
Loop
Sheets("Sheet2").Activate
Range("A1").Activate
WS1.Activate
End Sub

Try this code once - this is way too simpler and optimized processing than loops (slower)
Application.ScreenUpdating = False
Application.EnableEvents = False
Sheets("Application").AutoFilterMode = False
Dim lastrow, lastcol As Integer
lastrow = Range("F500000").End(xlUp).Row
lastcol = Sheets("Application").Range("A1").End(xlToRight).Column + 1
Sheets("Application").Cells(1, lastcol).Value = "helper"
Sheets("Application").Range(Sheets("Application").Cells(1, lastcol),Sheets("Application").Cells(lastrow, lastcol)).FormulaR1C1 = "=Right(RC[-1],2)"
Sheets("Application").Range(Range("A1"), Range("A1").End(xlToRight)).AutoFilter Field:=lastcol, Criteria1:="30"
Sheets("Application").Range(Range("A1"), Range("A1").End(xlToRight)).AutoFilter Field:=3, Criteria1:=">0"
Sheets("Application").Range(Cells(1, 1), Cells(lastrow, lastcol)).SpecialCells(xlCellTypeVisible).Copy Destination:=Sheet2.Range("A2")
Columns(lastcol).Delete
Application.ScreenUpdating = True
Application.EnableEvents = True

Here is the entire code. It works but takes ages to run. Any help to speed it up would be appreciated.
Sub Master()
Call compile1
Call compile2
End Sub
Sub compile1()
For Each cell In Sheets("Application").Range("C:C")
If Right(cell.Value, 2) = "10" Then
matchRow = cell.Row
Rows(matchRow & ":" & matchRow).Select
Selection.Copy
Sheets("Routine w credits").Select
ActiveSheet.Rows(matchRow).Select
ActiveSheet.Paste
Sheets("Application").Select
End If
Next
For Each cell In Sheets("Application").Range("C:C")
If Right(cell.Value, 2) = "20" Then
matchRow = cell.Row
Rows(matchRow & ":" & matchRow).Select
Selection.Copy
Sheets("Reactive w credits").Select
ActiveSheet.Rows(matchRow).Select
ActiveSheet.Paste
Sheets("Application").Select
End If
Next
End Sub
Sub compile2()
Set rSearch = Sheets("Application").Range("C:C")
For Each cell In rSearch
If Right(cell, 2) = "20" And cell.Offset(, 3) > 0 Then
matchRow = cell.Row
Rows(matchRow & ":" & matchRow).Select
Selection.Copy
Sheets("Reactive wo credits").Select
ActiveSheet.Rows(matchRow).Select
ActiveSheet.Paste
Sheets("Application").Select
End If
Next
For Each cell In rSearch
If Right(cell, 2) = "10" And cell.Offset(, 3) > 0 Then
matchRow = cell.Row
Rows(matchRow & ":" & matchRow).Select
Selection.Copy
Sheets("Routine wo credits").Select
ActiveSheet.Rows(matchRow).Select
ActiveSheet.Paste
Sheets("Application").Select
End If
Next
End Sub

Sub compile1()
Dim Cel As Range, Rng As Range
Set rSearch = Sheets("Application").Columns("C:C").SpecialCells(xlCellTypeConstants, 23)
For Each Cel In rSearch
If Right(Trim(Cel.Value), 2) = "30" And (Cel.Offset(, 3).Value > 0) Then
Cel.EntireRow.Copy
Sheets("Sheet2").Range("A" & Sheets("Sheet2").Range("C1048576").End(xlUp).Row + 1).Paste
Application.CutCopyMode = False
End If
Next
End Sub

Related

VBA check if columns are the same

I have two Sheets in Excel that I need to check if the columns are the same in both sheets before processing them.
I have created a macro to do this check, but I'm wondering if there is a better way to achieve this.
Sub CheckColumns()
Sheets("Source1").Select
Range("A1").Select
Range(Selection, Selection.End(xlToRight)).Select
Selection.Copy
Sheets("Sheet1").Select
Range("A1").Select
ActiveSheet.Paste
Sheets("Source2").Select
Range("A1").Select
Range(Selection, Selection.End(xlToRight)).Select
Selection.Copy
Sheets("Sheet1").Select
Range("A2").Select
ActiveSheet.Paste
Range("A3") = "=IF(A1=A2,0,1)"
Range("A3").Copy
Range("A2").Select
Selection.End(xlToRight).Select
ActiveCell.Offset(1, 0).Range("A1").Select
Range(Selection, Selection.End(xlToLeft)).Select
ActiveSheet.Paste
Range("A4") = "=SUM(3:3)"
If Range("A4").Value = 0 Then
MsgBox "Same Columns"
Else
MsgBox "different Columns"
End If
End Sub
First of all you need to avoid selection; How to avoid using Select in Excel VBA macros
Specificaally about your code; I would try comparing two arrays as it always faster to work with arrays and also it doesn't need a dummy-sheet. However, your approach, except the selection part is faster in my mind. So I would include the explicit version of your approach shortly.
Sub CheckColumns()
Dim arrS1 As Variant, arrS2 As Variant
Dim LastRow As Long
With Worksheets("Source1")
LastRow = .Cells(.Rows.Count, "A").End(xlUp).Row
arrS1 = .Range("A1:A" & LastRow)
End With
With Worksheets("Source2")
LastRow = .Cells(.Rows.Count, "A").End(xlUp).Row
arrS2 = .Range("A1:A" & LastRow)
End With
If UBound(arrS1) <> UBound(arrS2) Then
MsgBox "Different Columns"
Exit Sub
End If
same = True
For i = LBound(arrS1) to UBound(arrS1)
If arrS1(i) <> arrS1(i) Then
same = False
Exit For
End If
Next i
If same = True Then
MsgBox "Same Column"
Else
MsgBox "Item " & i & " does not match. Stopped checking further"
End If
End Sub
This is the explicit version of your method:
Sub CheckColumns()
Dim rngrS1 As Range, rngS2 As Range, rngSH As Range
Dim LastRow1 As Long, LastRow2 As Long
With Worksheets("Source1")
LastRow1 = .Cells(.Rows.Count, "A").End(xlUp).Row
Set rngS1 = .Range("A1:A" & LastRow)
End With
With Worksheets("Source2")
LastRow2 = .Cells(.Rows.Count, "A").End(xlUp).Row
Set rngS2 = .Range("A1:A" & LastRow)
End With
If LastRow1 <> LastRow2 Or rngS1(1) <> rngS2(1) Then
'Second condition checks names of the columns
MsgBox "Different Columns"
Exit Sub
End If
With Worksheets("Sheet1")
Set rngSH = .Range("A1:A" & LastRow1)
End With
rngSH.Value = rngS1.Value
Set rngSH = rngSH.Offset(0,1)
rngSH.Value = rngS2.Value
Set rngSH = rngSH.Offset(0,1)
rngSH.formula "=IF(A1=B1,0,1)"
Worksheets(Sheet1).Range("D2") = "Sum(C:C)"
If Worksheets(Sheet1).Range("D2").Value <> 0 Then
MsgBox "Different Columns"
Else
MsgBox "Same Columns"
End If
End Sub
You could declare two arrays and compare that way...
Sub Compare()
Dim FirstSheet As Variant, SecondSheet As Variant
Dim a As Long, b As Long
FirstSheet = Sheets("Source1").Range("A1:" & _
Mid(Sheets("Source1").Range("A1").End(xlToRight).Address, 2, _
InStr(Right(Sheets("Source1").Range("A1").End(xlToRight).Address, _
Len(Sheets("Source1").Range("A1").End(xlToRight).Address) - 2), "$")) & 1)
SecondSheet = Sheets("Source2").Range("A1:" & _
Mid(Sheets("Source2").Range("A1").End(xlToRight).Address, 2, _
InStr(Right(Sheets("Source2").Range("A1").End(xlToRight).Address, _
Len(Sheets("Source2").Range("A1").End(xlToRight).Address) - 2), "$")) & 1)
On Error Resume Next
For a = 1 To WorksheetFunction.Max(Sheets("Source1").Range("A1:" & _
Mid(Sheets("Source1").Range("A1").End(xlToRight).Address, 2, _
InStr(Right(Sheets("Source1").Range("A1").End(xlToRight).Address, _
Len(Sheets("Source1").Range("A1").End(xlToRight).Address) - 2), "$")) & 1).Cells.Count, _
Sheets("Source1").Range("A1:" & Mid(Sheets("Source1").Range("A1").End(xlToRight).Address, 2, _
InStr(Right(Sheets("Source1").Range("A1").End(xlToRight).Address, _
Len(Sheets("Source1").Range("A1").End(xlToRight).Address) - 2), "$")) & 1))
If FirstSheet(1, a) <> SecondSheet(1, a) Then b = b + 1
Next
On Error GoTo 0
If b = 0 Then
MsgBox "Same Columns"
Else
MsgBox "different Columns"
End If
End Sub

Populate three columns when form is saved

I have the following form which should be completed BEFORE the data is submitted to the sheet:
I am trying to code it so upon completion of the form and when the save button is clicked, it will find the first empty cell in column A and copy down the formula from above, I have managed to do this successfully but now I want to tab into column B and copy down the formula from above like before.
THEN tab into column C and enter the data in order from the form I have created into adjacent cells.
Here is my code but I really am in the dark here!
Private Sub CommandButton2_Click()
Dim irow As Long
Dim ws As Worksheet
Set ws = Worksheets("Master Data")
NextFree = Range("A10:A" & Rows.Count).Cells.SpecialCells(xlCellTypeBlanks).Row
Range("A" & NextFree).Select
' Copy formula from cell above
Dim oCell As Range
For Each oCell In Selection
If (oCell.Value = "") Then
oCell.Offset(-1, 0).Copy Destination:=oCell
End If
Next oCell
End Sub
' Move to adjacent cell
Sub MoveOver()
ActiveCell.Offset(0, 1).Select
End Sub
'Insert data into cells
Sub LastRow()
.Offset(1, 0) = ComboBox1.Text
.Offset(1, 1) = TextBox1.Value
.Offset(1, 2) = TextBox2.Value
.Offset(1, 3) = TextBox3.Value
.Offset(1, 4) = TextBox4.Value
.Offset(1, 5) = TextBox5.Value
.Offset(1, 6) = TextBox6.Value
End Sub
Could it be something like this you are looking for?
Dim lstRw As Long
lstRw = Cells(Rows.Count, "A").End(xlUp).Row
Range("A" & lstRw & ":B" & lstRw).Copy Range("A" & lstRw + 1)
You can keep it in the same sub, such as
Dim lstRw As Long
Dim Rng As Range
lstRw = Cells(Rows.Count, "A").End(xlUp).Row
Set Rng = Range("A" & lstRw + 1)
Range("A" & lstRw & ":B" & lstRw).Copy Range("A" & lstRw + 1)
With Rng
.Offset(, 2) = ComboBox1.Text
.Offset(, 3) = TextBox1.Value
.Offset(, 4) = TextBox2.Value
.Offset(, 5) = TextBox3.Value
.Offset(, 6) = TextBox4.Value
.Offset(, 7) = TextBox5.Value
.Offset(, 8) = TextBox6.Value
End With

Insert row in excel with a value in a specific cell

I'm using this script to insert fill with rows where non-sequential is produced in a column of an excel file.
Sub InsertValueBetween()
Dim lastrow As Long
Dim gap As Long
Dim i As Long, ii As Long
Application.ScreenUpdating = False
With ActiveSheet
lastrow = .Cells(.Rows.Count, "A").End(xlUp).Row
For i = lastrow To 3 Step -1
gap = .Cells(i, "A").Value - .Cells(i - 1, "A").Value
If gap > 1 Then
.Rows(i).Resize(gap - 1).Insert
End If
Next i
lastrow = .Cells(.Rows.Count, "A").End(xlUp).Row
.Cells(3, "A").Value = .Cells(2, "A").Value + 1
.Cells(2, "A").Resize(2).AutoFill .Cells(2, "A").Resize(lastrow - 1)
End With
End Sub
In addition to adding these new rows I want them to also have a specific value in column B. I'm trying to implement this but with no result.
Anybody could help me?
One way you could tackle this challenge is with a Range variable. Here is some heavily-commented code that walks through the process:
Sub InsertValueBetweenRev2()
Dim Target As Range '<~ declare the range variable
'... declare your other variables
'... do other stuff
For i = lastrow To 3 Step -1
gap = .Cells(i, "A").Value - .Cells(i - 1, "A").Value
If gap > 1 Then
.Rows(i).Resize(gap - 1).Insert
'the next line sets the range variable to the recently
'added cells in column B
Set Target = .Range(.Cells(i, 2), .Cells(i + gap - 2, 2))
Target.Value = "Cool" '<~ this line writes text "Cool" into those cells
End If
Next i
'... the rest of your code
End Sub
So, to sum it up, we know that gap - 1 rows are going to be added, and we know that the new rows are added starting at row i. Using that knowledge, we assign the just-added cells in column B to a Range then set the .value of that Range to whatever is needed.
a Better way of doing it with less variables and faster:
Sub InsRowWithText()
Dim LR As Long, i As Long
LR = Range("D" & Rows.Count).End(xlUp).row
For i = LR To 3 Step -1
If Range("D" & i).Value <> Range("D" & i - 1).Value Then
Rows(i).Resize(1).Insert
Range("D" & i).Value = "Test"
End If
Next i
End Sub
This is how i utilized it:
Sub InsRowWithText()
Dim strMsg As String, strTitle As String
Dim LR As Long, i As Long
Text = "ADD"
strMsg = "Warning: This is a Advanced Function, Continue? "
strTitle = "Warning: Activated Advanced Function "
If MsgBox(strMsg, vbQuestion + vbYesNo, strTitle) = vbNo Then
Exit Sub
Else
Sheets("SAP Output DATA").Select
If Range("D3").Value = Text Then
MsgBox "Detected That This Step Was Already Completed, Exiting."
Exit Sub
End If
application.ScreenUpdating = False
LR = Range("D" & Rows.Count).End(xlUp).row
For i = LR To 3 Step -1
If Range("D" & i).Value <> Range("D" & i - 1).Value Then
Rows(i).Resize(1).Insert
Range("D" & i).EntireRow.Interior.ColorIndex = xlColorIndexNone
Range(("A" & i), ("D" & i)).Value = Text
End If
Next i
End If
Range("D2").Select
Selection.End(xlDown).Select
ActiveCell.Offset(1).Select
Range(("A" & ActiveCell.row), ("D" & ActiveCell.row)).Value = Text 'last row doesnt get text for some reason.
ActiveCell.EntireRow.Interior.ColorIndex = xlColorIndexNone
ActiveCell.Offset(1).Select
Range(("D" & ActiveCell.row), ("E" & ActiveCell.row)).Interior.ColorIndex = 17 'purple
application.ScreenUpdating = True
Range("D3").Select
End Sub

comparing a single value against an array in VBA

Sub CHECKas()
Dim lastrow As Long
Dim lastcol As Long
Dim l As Integer
Dim i As Integer
Dim rname As Constants
Set rngTarg = Selection
lastrow = Sheets("report").Range("B" & Rows.Count).End(xlUp).row
lastcol = Sheets("report").Cells(2, Columns.Count).End(xlToLeft).Column
Sheets("FEBBRAIO").Select
ActiveCell.Offset(0, -3).Copy
Sheets("REPORT").Select
Cells(1, lastcol + 1).PasteSpecial xlPasteAll
Application.CutCopyMode = False
rname = Application.ActiveCell.Value
ActiveCell.Offset(1, 0).Select
For i = 2 To lastrow
ThisWorkbook.Sheets("report").Select
If Range("f2:f" & lastrow) <= Val(CStr(rname.Value)) _
And Range("g2:g" & lastrow) > Val(CStr(rname.Value)) Then
Cells(i, ActiveCell.Column).Value = "1"
Else
Cells(i, ActiveCell.Column).Value = 0
End If
Next i
End Sub
I'm new in VBA and I can't understand how to compare a constant value with each cell in a range("g2:g" & lastrow) and ("f2:f" & lastrow). The constant value is an active cell in my case. For example considering this formula: IF(AND($R$1<G2;$R$1>=f2);1;0 where R$1$ is the active cell of the last not empty column in ROW 1. I need to fill the entire column (that is activecell.column) with the output coming out form this formula.
But the I Got mismatch error in:
If Range("f2:f" & lastrow) <= Val(CStr(rname.Value)) _
And Range("g2:g" & lastrow) > Val(CStr(rname.Value)) Then
Cells(i, ActiveCell.Column).Value = "1"
Else
Cells(i, ActiveCell.Column).Value = 0
End If
I know from the previous question that this error occurs because I'm trying to comparing a single value against an array of values. How can fix this problem?
You have to use
Range("F" & i)
in your code. Same thing applies to other instances of such code.

How to split and restructure cells using excel VBA

The code I currently use splits:
And changes it to:
However, this is the format in which I require the data to be in:
This is a copy of my current code:
Sub SplitCells()
Dim rColumn As Range
Dim lFirstRow As Long
Dim lLastRow As Long
Dim lRow As Long
Dim lLFs As Long
Set rColumn = Columns("D")
lFirstRow = 1
lLastRow = rColumn.Cells(Rows.Count).End(xlUp).Row
For lRow = lLastRow To lFirstRow Step -1
lLFs = Len(rColumn.Cells(lRow)) - Len(Replace(rColumn.Cells(lRow), vbLf, ""))
If lLFs > 0 Then
rColumn.Cells(lRow + 1).Resize(lLFs).EntireRow.Insert xlShiftDown
rColumn.Cells(lRow).Resize(lLFs + 1).Value = Application.Transpose(Split(rColumn.Cells(lRow), vbLf))
End If
Next lRow
End Sub
Any help/comments will be appreciated.
call ResizeToFit macro at the end of your code
Add ResizeToFit right before End Sub in your current code
ie.
...
Next lRow
ResizeToFit ' or Call ResizeToFit
End Sub
...
add this code to the same module as a new sub
Sub ResizeToFit()
Application.ScreenUpdating = False
Dim i As Long
For i = Range("D" & Rows.Count).End(xlUp).Row To 1 Step -1
If IsEmpty(Range("D" & i)) Then
Rows(i & ":" & i).Delete
Else
Range("E" & i) = Split(Range("D" & i), Chr(32))(1)
Range("D" & i) = Split(Range("D" & i), Chr(32))(0)
End If
Next i
For i = 1 To 5
If i <> 4 Then
Cells(1, i).Resize(Range("D" & Rows.Count).End(xlUp).Row, 1).Value = Cells(1, i)
End If
Next
Application.ScreenUpdating = True
End Sub
Taking THIS
and running my code produces
Sub SplitCells()
Dim rColumn As Range
Dim lFirstRow As Long
Dim lLastRow As Long
Dim lRow As Long
Dim lLFs As Long
Set rColumn = Columns("D")
lFirstRow = 1
lLastRow = rColumn.Cells(Rows.Count).End(xlUp).Row
For lRow = lLastRow To lFirstRow Step -1
lLFs = Len(rColumn.Cells(lRow)) - Len(Replace(rColumn.Cells(lRow), vbLf, ""))
If lLFs > 0 Then
rColumn.Cells(lRow + 1).Resize(lLFs).EntireRow.Insert xlShiftDown
rColumn.Cells(lRow).Resize(lLFs + 1).Value = Application.Transpose(Split(rColumn.Cells(lRow), vbLf))
End If
Dim curRow As Integer
curRow = lRow + lLFs
While curRow >= lRow
If Application.CountA(Rows(curRow).EntireRow) = 0 Then
Rows(curRow).Delete
Else
rColumn.Cells(curRow).Offset(0, 1).Value = Split(rColumn.Cells(curRow), " ")(1)
rColumn.Cells(curRow).Value = Split(rColumn.Cells(curRow), " ")(0)
rColumn.Cells(curRow).Offset(0, -3).Value = rColumn.Cells(lRow).Offset(0, -3).Value
rColumn.Cells(curRow).Offset(0, -2).Value = rColumn.Cells(lRow).Offset(0, -2).Value
rColumn.Cells(curRow).Offset(0, -1).Value = rColumn.Cells(lRow).Offset(0, -1).Value
End If
curRow = curRow - 1
Wend
Next lRow
End Sub
This is just from a recorded macro so it needs cleaning up.
ActiveCell.FormulaR1C1 = "=LEFT(RC[-1],LEN(RC[-1])-5)"
Range("E1:E4").Select
Selection.FillDown
Range("F1").Select
ActiveCell.FormulaR1C1 = "=RIGHT(RC[-2],4)"
Range("F1:F4").Select
Selection.FillDown
Range("E1:F4").Select
Selection.Copy
Range("E1").Select
Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=False
Columns("D:D").Select
Application.CutCopyMode = False
Selection.Delete Shift:=xlToLeft
You may not need the cut, paste and column delete if you're happy with Column D staying as it is and having the split parts to the right. In which case
ActiveCell.FormulaR1C1 = "=LEFT(RC[-1],LEN(RC[-1])-5)"
Range("E1:E4").Select
Selection.FillDown
Range("F1").Select
ActiveCell.FormulaR1C1 = "=RIGHT(RC[-2],4)"
Range("F1:F4").Select
Selection.FillDown
Sorry - ActiveCell is E1.