I'm currently using Excel 2010 and am trying to run some code I put together in VBA for Applications (after hitting alt+F11). I typed up the code in a notepad that appeared after double clicking the project I wanted to work on. I also saved everything as Excel Macro Enabled Workbook (*.xlsm).
I am trying to color the backgrounds of Column D either green or red if columns S, T, and U meet the criteria. If the columns all have a value of 0 then Cell D should be colored green. If not, it should be colored red.
Sub GreenOrRed()
Dim i As Integer
For i = 2 To i = 27293
If (Cells(i, "S").Value = 0 And Cells(i, "T").Value = 0 And Cells(i, "U").Value = 0) Then
Cells(i, "D").Interior.ColorIndex = 10
Else
Cells(i, "D").Interior.ColorIndex = 9
End If
Next i
End Sub
The code runs and doesn't throw any error but it also doesn't do anything. What am I doing wrong?
You are using counter in For loop incorrectly. It should be like this...
For i = 2 To 27293
Changed For condition.
Try this:-
Sub GreenOrRed()
Dim i As Integer
For i = 2 To 27293
If (Cells(i, "S").Value = 0 And Cells(i, "T").Value = 0 And Cells(i, "U").Value = 0) Then
Cells(i, "D").Interior.ColorIndex = 10
Else
Cells(i, "D").Interior.ColorIndex = 9
End If
Next i
End Sub
A slightly different approach:
Sub GreenOrRed()
Dim r As Range, rr As Range
Set rr = Range("D1:D27293")
For Each r In rr
If r.Offset(0, 15).Value = 0 And r.Offset(0, 16).Value = 0 And r.Offset(0, 17).Value = 0 Then
r.Interior.ColorIndex = 10
Else
r.Interior.ColorIndex = 9
End If
Next r
End Sub
You might consider setting one (or two) conditional formatting rules.
Option Explicit
Sub GreenOrRed()
With ActiveSheet
With .Range(.Cells(2, "D"), .Cells(.Rows.Count, "D").End(xlUp))
.Interior.ColorIndex = 9
.FormatConditions.Delete
With .FormatConditions.Add(Type:=xlExpression, Formula1:="=and(sum($S2)=0, sum($T2)=0, sum($U2)=0)")
.Interior.ColorIndex = 10
.StopIfTrue = True
End With
End With
End With
End Sub
I've used individual SUM functions to ensure that any text returns a numerical value of zero.
Alternate AutoFilter method.
Sub GreenOrRedFiltered()
With ActiveSheet
If .AutoFilterMode Then .AutoFilterMode = False
With .Range(.Cells(1, "D"), .Cells(.Rows.Count, "D").End(xlUp)).Resize(, 18)
.Resize(.Rows.Count - 1, .Columns.Count).Offset(1, 0).Columns(1).Interior.ColorIndex = 9
.AutoFilter Field:=16, Criteria1:=0, Operator:=xlOr, Criteria2:=vbNullString
.AutoFilter Field:=17, Criteria1:=0, Operator:=xlOr, Criteria2:=vbNullString
.AutoFilter Field:=18, Criteria1:=0, Operator:=xlOr, Criteria2:=vbNullString
With .Resize(.Rows.Count - 1, .Columns.Count).Offset(1, 0)
If CBool(Application.Subtotal(103, .Cells)) Then
.Columns(1).SpecialCells(xlCellTypeVisible).Interior.ColorIndex = 10
End If
End With
End With
If .AutoFilterMode Then .AutoFilterMode = False
End With
End Sub
Related
I'm very new to Excel VBA (started about a day ago!) but I'm slowly struggling through. I've created a formula that copies a selection of three cells to another part of the sheet if column D contains the value "(2)", then assigns the value "0" to some more cells in the same row.
The trouble is, I've used a mixture of recording and typing my macro so the end result is pretty messy. Currently the macro takes a while to complete (it moves everything around and then a little hourglass appears for a good 15 seconds or so). I'm assuming this is in part due to my use of "Select" (I'm aware this is a bad thing!) but I'm just trying to work out what I can strip from the formula to make it more efficient while retaining the same outcome.
Sub MoveNames()
Dim SrchRng As Range, cel As Range
Set SrchRng = Range("D:D")
For Each cel In SrchRng
If InStr(1, cel.Value, "(2)") > 0 Then
cel.Offset(0, 1).Range("A1:C1").Select
Selection.Copy
ActiveCell.Offset(-1, 40).Range("A1").Select
ActiveSheet.Paste
Application.CutCopyMode = False
ActiveCell.Offset(0, -4) = "0"
ActiveCell.Offset(0, -5) = "0"
ActiveCell.Offset(0, -6) = "0"
ActiveCell.Offset(0, -7) = "0"
ActiveCell.Offset(0, -10) = "0"
ActiveCell.Offset(0, -12) = "0"
End If
Next cel
End Sub
Any help would be much appreciated.
If I understand what you're trying to do, this should do the same thing without having to use any objects or any copy/paste methods:
Sub MM_MoveNames()
For i = 2 To Cells(Rows.count, 4).End(xlUp).Row
If InStr(Cells(i, 4).value, "(2)") Then
Cells(i - 1, 44).Resize(1, 3).value = Cells(i, 5).Resize(1, 3).value
Cells(i, 37).Resize(1, 4).value = 0
Cells(i, 34).value = 0
Cells(i, 32).value = 0
End If
Next
End Sub
More importantly though - if your code is working, and you just want advice for improvements then you should post your code on Code Review, not on Stack Overflow.
try this
Sub MoveNames()
Dim SrchRng As Range
lastrow = Range("D" & Rows.Count).End(xlUp).Row
Set SrchRng = Range("D1:D" & lastrow)
For Each cel In SrchRng
If InStr(1, cel.Value, "(2)") > 0 Then
With cel.Offset(0, 1).Range("A1:C1")
.Copy cel.Offset(-1, 40).Range("A1")
End With
With cel.Offset(-1, 40)
.Offset(0, -4) = "0"
.Offset(0, -5) = "0"
.Offset(0, -6) = "0"
.Offset(0, -7) = "0"
.Offset(0, -10) = "0"
.Offset(0, -12) = "0"
End With
End If
Next cel
End Sub
Give this a shot, you can definitely clean it up more by combining the multiple offsets and ranges.
Sub test()
Dim rngIndex As Range
For Each rngIndex In Range("D:D")
If InStr(1, rngIndex.Value, "(2)") > 0 Then
rngIndex.Offset(0, 1).Range("A1:C1").Copy _
rngIndex.Offset(0, 1).Range("A1:C1").Offset(-1, 40).Range("A1")
With rngIndex.Offset(0, 1).Range("A1:C1")
Range(.Offset(0, -4), .Offset(0, -7)).Value = 0
.Offset(0, -10) = "0"
.Offset(0, -12) = "0"
End With
End If
Next rngIndex
End Sub
Instead of going throug each cell in column D, you can go through just the used range, like this:
Set SrchRng = Range("D1:D" & ActiveSheet.UsedRange.Rows.Count)
Which should speed it up quite a bit.
You can use Select, I found that easier when I was learning VBA myself. In time you will learn to avoid it.
To speed up macro execution when using Select, you can add Application.ScreenUpdating = False at the beginning and Application.ScreenUpdating = True at the end of your procedure.
Disabling automatic calculations is also beneficial, you can do it by adding Application.Calculation = xlManual and Application.Calculation = xlManual at the beginning and end respectively.
Hope that helps. if you have more questions, just ask.
My turn - instead of looking at each cell, just jump to the ones containing (2).
Sub MoveNames()
Dim SrchRng As Range, cel As Range
Dim rFound As Range
Dim sFirstAddress As String
Set SrchRng = ThisWorkbook.Worksheets("Sheet1").Range("D:D")
Set rFound = SrchRng.Find("(2)", LookIn:=xlValues, LookAt:=xlPart, SearchDirection:=xlNext)
If Not rFound Is Nothing Then
sFirstAddress = rFound.Address
Do
rFound.Offset(, 1).Resize(, 3).Copy Destination:=rFound.Offset(-1, 41)
rFound.Offset(-1, 34).Resize(, 4) = 0
rFound.Offset(-1, 29) = 0
rFound.Offset(-1, 31) = 0
Set rFound = SrchRng.FindNext(rFound)
Loop While Not rFound Is Nothing And rFound.Address <> sFirstAddress
End If
End Sub
I've been trying to add the entire row that meets the highlight criteria to an array but I've been struggling getting it to work.
The code loops through multiple identifiers and highlight them in red based off of the preconditions. I would like to add the entire row to an array for all rows meeting the precondition criteria.
Sub SWAPS101()
'red color
' If "Security Type" = SW
' If "New Position Ind" = N
' If "Prior Price" = 100
' If "Current Price" does not equal 100
Dim rng As Range, lCount As Long, LastRow As Long
Dim cell As Object
'Sheets("Output").Activate
With ActiveSheet
LastRow = .Cells(Rows.Count, 1).End(xlUp).Row
For Each cell In .Range("E2:E" & LastRow) 'new position
If cell = "N" And cell.Offset(, 16) = "SW" And cell.Offset(, 5) = 100 _
And cell.Offset(, 4) <> 100 Then
With cell.EntireRow.Interior
.Pattern = xlSolid
.PatternColorIndex = xlAutomatic
.Color = 6382079
.TintAndShade = 0
.PatternTintAndShade = 0
End With
' LastRow = Range("b65000").End(xlUp).Row
' For r = 2 To LastRow
Row = Row + 1
TempArray(Row, 1) = Cells(r, cell))
Next r
End If
Next cell
End With
End Sub
Using the Range.CurrentRegion property to isolate the 'island' of data radiating out from A1 is an easy method to restrict the 'scope' of the operation. You do not want to be copying thousands of blank cells into an array.
Sub SWAPS101()
'red color
' If "Security Type" = SW
' If "New Position Ind" = N
' If "Prior Price" = 100
' If "Current Price" does not equal 100
Dim a As Long, r As Long, c As Long, vVALs As Variant
With Sheets("Output")
'reset the environment
If .AutoFilterMode Then .AutoFilterMode = False
.Columns(5).Interior.Pattern = xlNone
With .Cells(1, 1).CurrentRegion
ReDim vVALs(1 To .Columns.Count, 1 To 1)
.AutoFilter field:=Application.Match("security type", .Rows(1), 0), Criteria1:="SW"
.AutoFilter field:=Application.Match("new position ind", .Rows(1), 0), Criteria1:="N"
.AutoFilter field:=Application.Match("prior price", .Rows(1), 0), Criteria1:=100
.AutoFilter field:=Application.Match("current price", .Rows(1), 0), Criteria1:="<>" & 100
With .Resize(.Rows.Count - 1, .Columns.Count).Offset(1, 0)
'check to ensure that there is something to work with
If CBool(Application.Subtotal(103, .Cells)) Then
With Intersect(.Columns(5), .SpecialCells(xlCellTypeVisible))
.Cells.Interior.Color = vbRed
End With
Debug.Print .SpecialCells(xlCellTypeVisible).Areas.Count
With .SpecialCells(xlCellTypeVisible)
For a = 1 To .Areas.Count
Debug.Print .Areas(a).Rows.Count
For r = 1 To .Areas(a).Rows.Count
Debug.Print .Areas(a).Rows(r).Address(0, 0)
ReDim Preserve vVALs(1 To UBound(vVALs, 1), 1 To UBound(vVALs, 2) + 1)
For c = 1 To .Columns.Count
vVALs(c, UBound(vVALs, 2)) = _
.Areas(a).Rows(r).Cells(1, c).Value
Next c
Next r
Next a
vVALs = Application.Transpose(vVALs)
End With
'array is populated - do something with it
Debug.Print LBound(vVALs, 1) & ":" & UBound(vVALs, 1)
Debug.Print LBound(vVALs, 2) & ":" & UBound(vVALs, 2)
'this dumps the values starting a couple of rows down
With .Cells(.Rows.Count, 1).Offset(3, 0)
.Resize(UBound(vVALs, 1), UBound(vVALs, 2)) = vVALs
End With
End If
End With
End With
If .AutoFilterMode Then .AutoFilterMode = False
End With
End Sub
I've left a lot of the debug.print statements in so you can watch how the process loops through the rows of each Range.Areas property within the Range.SpecialCells method's xlCellTypeVisible set. Use F8 to step through the code while keeping an eye on the VBE's Immediate window ([Ctrl]+G).
Post-processing results
You can add ranges to an array, such as:
Dim myArray() As Variant 'declare an unallocated array.
myArray = Range("E2:E" & LastRow) 'myArray is now an allocated array, range being your row
My idea is to create union range uRng but I couldn't fill it in array so create temp sheet and past this range in it then fill the selection (the copied range) in array then delete this temp sheet.
this will work but I don't know if it is good way so this is just an idea because Jeeped answer seems the full answer for this question
Sub SWAPS101()
'red color
' If "Security Type" = SW
' If "New Position Ind" = N
' If "Prior Price" = 100
' If "Current Price" does not equal 100
Dim rng As Range, lCount As Long, LastRow As Long
Dim cell As Range
Dim TempArray As Variant, uRng As Range, tempSH As Worksheet
'Sheets("Output").Activate
With ActiveSheet
LastRow = .Cells(Rows.Count, 1).End(xlUp).Row
For Each cell In .Range("E2:E" & LastRow) 'new position
If cell = "N" And cell.Offset(, 16) = "SW" And cell.Offset(, 5) = 100 _
And cell.Offset(, 4) <> 100 Then
With cell.EntireRow.Interior
.Pattern = xlSolid
.PatternColorIndex = xlAutomatic
.Color = 6382079
.TintAndShade = 0
.PatternTintAndShade = 0
End With
If uRng Is Nothing Then
Set uRng = cell.EntireRow
Else
Set uRng = Union(uRng, cell.EntireRow)
End If
End If
Next cell
End With
If Not uRng Is Nothing Then
Application.ScreenUpdating = False
Set tempSH = Sheets.Add
uRng.Copy
tempSH.Paste
TempArray = Selection.Value
Application.DisplayAlerts = False
tempSH.Delete
Application.DisplayAlerts = True
Application.ScreenUpdating = True
End If
End Sub
If column A contains "RR" and column C is not equal to "memo" and column E is not equal to "Air" or "Printed" then column L=0.
Then followed by If column A contains "RR" and column C is not equal to "memo" and column E is equal to "Air" or "Printed" then column L= is column H*.1.
I believe I am having trouble with equals/not equals.
Sub RRClean()
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
Dim myString As String
RowCount = WorksheetFunction.CountA(range("A:A"))
For i = 2 To RowCount
myString = Trim(Cells(i, 2).Value)
If InStr(myString, "RR") > 0 And .cell(i, 3) <> "Memo" And .cell(i, 7) <> "Air" Or .cell(i, 7) <> "Printed" Then
Cells(i, 12).Value = 0
End If
Next
For i = 2 To RowCount
myString = Trim(Cells(i, 2).Value)
If InStr(myString, "RR") > 0 And .cell(i, 3) <> "Memo" And .cell(i, 7) = "Air" Or .cell(i, 7) = "Printed" Then
Cells(i, 12).Value = cell(i, 8) * 0.1
End If
Next
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
End Sub
Your logic is slightly flawed when dealing with "and column E is not equal to "Air" or "Printed"". If a cell is not Air it could be Printed. If it is not Printed then it could be Air. You need And here as in "and column E is not equal to "Air" and column E is not equal to "Printed".
Sub Cmemo()
With Worksheets("Sheet4") '<~~ SET THIS WORKSHEET REFERENCE PROPERLY!!
If .AutoFilterMode Then .AutoFilterMode = False
With .Cells(1, 1).CurrentRegion
.AutoFilter Field:=1, Criteria1:="*RR*"
.AutoFilter Field:=3, Criteria1:="<>memo"
.AutoFilter Field:=5, Criteria1:="<>Air", _
Operator:=xlAnd, Criteria2:="<>Printed"
If CBool(Application.Subtotal(103, .Offset(1, 0).Cells)) Then
With .Resize(.Rows.Count - 1, 1).Offset(1, 11)
.SpecialCells(xlCellTypeVisible) = 0
End With
End If
.AutoFilter Field:=5, Criteria1:="Air", _
Operator:=xlOr, Criteria2:="Printed"
If CBool(Application.Subtotal(103, .Offset(1, 0).Cells)) Then
With .Resize(.Rows.Count - 1, 1).Offset(1, 11)
.SpecialCells(xlCellTypeVisible).FormulaR1C1 = "=rc[-4]/10"
End With
End If
End With
If .AutoFilterMode Then .AutoFilterMode = False
End With
End Sub
Using the AutoFilter Method, both operations can be accomplished without declaring a single variable.
I am trying to run the below code. But it is showing the error of Subscript out of range. When I tried to debug it, it is showing error in the 5 line: Range(“A1”).Select
While debugging, when I made the Sheet1 of 4th line as Sheet2, then it is not going on Sheet2.
Please help me run the code properly.
Sub excelmacro()
Application.ScreenUpdating = False
Sheets(“Sheet1”).Select
Range(“A1”).Select
Sheets(“Sheet2”).Select
Range(“A2”).Select
For i = 1 To 3
Sheets(“Sheet1”).Select
If Len(ActiveCell.Value) > 1 Then
Sheets(“Sheet1”).Select
Xname = Right(ActiveCell.Value, Len(ActiveCell.Value) - 6)
Xdesig = Right(ActiveCell.Offset(1, 0).Value, Len(ActiveCell.Offset(1, 0).Value) - 13)
Xsalary = Right(ActiveCell.Offset(2, 0).Value, Len(ActiveCell.Offset(2, 0).Value) - 8)
Sheets(“Sheet2”).Select
ActiveCell.Value = Xname
ActiveCell.Offset(0, 1).Value = Xdesig
ActiveCell.Offset(0, 2).Value = Xsalary
ActiveCell.Offset(1, 0).Select
Sheets(“Sheet1”).Select
ActiveCell.Offset(3, 0).Select
Else
i = 10
End If
i = i - 1
Next
Application.ScreenUpdating = True
End Sub
The quotation marks are oddball and create an error, but even after changing to 'normal' quoates there is a Subscript out of range error:
Instead of using Sheets, try Worksheets:
Worksheets("Sheet1").Select
To summarize my comments:
The double-quotes in the original code are oddly formatted. Use Notepad or the VBA IDE to replace them with appropriate plain text double quotes.
Be sure to declare your variables before using them if Option Explicit is turned on. Also just a good practice to follow even if it were not on.
(To be updated when I have more time this evening) Avoid making selections and usingActiveCell/ActiveSheet references.
With minor changes to your code it should look like this:
Sub excelmacro()
Dim i As Double, _
Xname As String, _
Xdesig As String, _
Xsalary As String
Application.ScreenUpdating = False
Sheets("Sheet1").Select
Range("A1").Select
Sheets("Sheet2").Select
Range("A2").Select
For i = 1 To 3
Sheets("Sheet1").Select
If Len(ActiveCell.Value) > 1 Then
Sheets("Sheet1").Select
Xname = Right(ActiveCell.Value, Len(ActiveCell.Value) - 6)
Xdesig = Right(ActiveCell.Offset(1, 0).Value, Len(ActiveCell.Offset(1, 0).Value) - 13)
Xsalary = Right(ActiveCell.Offset(2, 0).Value, Len(ActiveCell.Offset(2, 0).Value) - 8)
Sheets("Sheet2").Select
ActiveCell.Value = Xname
ActiveCell.Offset(0, 1).Value = Xdesig
ActiveCell.Offset(0, 2).Value = Xsalary
ActiveCell.Offset(1, 0).Select
Sheets("Sheet1").Select
ActiveCell.Offset(3, 0).Select
Else
i = 10
End If
i = i - 1
Next
Application.ScreenUpdating = True
End Sub
I think this is what you're trying to do:
Sub excelmacro()
Dim lastrowinSheet1 As Long
Dim cellinSheet2 As Range
Dim rCell As Range
Dim x As Long
With ThisWorkbook
'Set a reference to cell A1 on Sheet2.
Set cellinSheet2 = .Worksheets("Sheet2").Range("A1")
With .Worksheets("Sheet1")
'This will return the last row number containing data in column A.
lastrowinSheet1 = .Cells(Rows.Count, 1).End(xlUp).Row
'Now loop through each cell in column A of sheet1.
For x = 1 To lastrowinSheet1
If Len(.Cells(x, 1)) > 1 Then
cellinSheet2.Value = Right(.Cells(x, 1).Value, Len(.Cells(x, 1).Value) - 6)
cellinSheet2.Offset(, 1) = Right(.Cells(x, 1).Offset(1).Value, Len(.Cells(x, 1).Offset(1).Value) - 13)
cellinSheet2.Offset(, 2) = Right(.Cells(x, 1).Offset(2).Value, Len(.Cells(x, 1).Offset(2).Value) - 8)
Set cellinSheet2 = cellinSheet2.Offset(1)
x = x + 2
End If
Next x
End With
End With
End Sub
I tried taking apart your code - I think this is what it's doing:
Sub excelmacro1()
'Stop the screen flicker.
Application.ScreenUpdating = False
'Select cell A1 on Sheet1.
Sheets(“Sheet1”).Select
Range(“A1”).Select
'Select cell A2 on sheet 2.
Sheets(“Sheet2”).Select
Range(“A2”).Select
For i = 1 To 3
'Select Sheet1 again.
Sheets(“Sheet1”).Select
'If the length of text in the ActiveCell is greater than 1 character then
'execute the lines up to ELSE.
If Len(ActiveCell.Value) > 1 Then
'Select Sheet1 yet again.
Sheets(“Sheet1”).Select
'Hope the value in the ActiveCell isn't longer than 6 digits, or it will error out.
'Take all characters from the ActiveCell except the last 6.
Xname = Right(ActiveCell.Value, Len(ActiveCell.Value) - 6)
'Take all characters from the ActiveCell except the last 13.
Xdesig = Right(ActiveCell.Offset(1, 0).Value, Len(ActiveCell.Offset(1, 0).Value) - 13)
'Take all characters from the ActiveCell except the last 8.
Xsalary = Right(ActiveCell.Offset(2, 0).Value, Len(ActiveCell.Offset(2, 0).Value) - 8)
'Select Sheet2.
Sheets(“Sheet2”).Select
'Place the values in ActiveCell and the two columns to the right.
ActiveCell.Value = Xname
ActiveCell.Offset(0, 1).Value = Xdesig
ActiveCell.Offset(0, 2).Value = Xsalary
'Select the next row down.
ActiveCell.Offset(1, 0).Select
'Active Sheet1 again.
Sheets(“Sheet1”).Select
'Select the cell 3 rows down from the previous row.
ActiveCell.Offset(3, 0).Select
Else
'If the lengh of text in the ActiveCell is 1 character or less then set the value of i to 10.
i = 10
End If
'Remove 1 from i.
i = i - 1
Next
Application.ScreenUpdating = True
End Sub
I have a pickle that I was hoping to get some help with. I cobbled together a macro the other day following this logic:
If a cell in column B has a particular text, "brand1", and that row has the value "y" in column U, then populate column W of that row with a text string, "sample1".
I had it working the other day, but for some reason it's not working today. Any ideas or thoughts? Totally open to the idea of scrapping this macro anyway and using a different one.
Sub PromoID()
Application.ScreenUpdating = False
Dim lastRow As Long
Dim cell As Range
lastRow = Range("B" & Rows.Count).End(xlUp).Row
For Each cell In Range("B2:B" & lastRow)
If InStr(1, cell.Value, "Brand1") <> 0 Then
InStr(1, cell.Offset(, 21).Value, "y") <> 0 Then
cell.Offset(, 23).Value = "sample1"
End If
End If
Next
Application.ScreenUpdating = True
End Sub
You were not that far from the answer. Keep your eyes peeled for minor errors.
Sub PromoID()
Application.ScreenUpdating = False
Dim lastRow As Long
Dim cell As Range
lastRow = Range("B" & Rows.Count).End(xlUp).Row + 1
For Each cell In Range("B2:B" & lastRow)
If InStr(1, cell.Value, "Brand1") <> 0 Then
If InStr(1, cell.Offset(0, 19).Value, "y") <> 0 Then
cell.Offset(0, 21).Value = "sample1"
End If
End If
Next
Application.ScreenUpdating = True
End Sub
Output:
Try to make everything uppercase. With Ucase()
If ucase(InStr(1, cell.Value, "Brand1") ) <> 0 Then ucase(InStr(1, cell.Offset(, 21).Value, "y") ) <> 0 Then cell.Offset(, 23).Value = "sample1"