I am trying to make a sub where the macro counts all the rows containing data (it isn't a set amount, it varies) and then picks a random number between 6 (to exclude headers) and the number of counted rows and highlights the corresponding row. Unfortunately the code I've been working on doesn't do what I need it to do. It does select and highlight a random row, but it selects empty rows too, not only the ones with data. Any ideas where I went wrong?
Sub RandomRow()
Dim mrg As Worksheet
Dim Count As Integer, myRange As Range
Set mrg = ActiveWorkbook.Sheets("Merged")
mrg.Range("A6:K200000").Interior.Color = RGB(255, 255, 255) 'reset cell colours
Set myRange = mrg.Columns("A:A")
Count = Application.WorksheetFunction.CountA(myRange)
myValue = Int((Count * Rnd) + 6) ' Generate random value between 6 and the number of filled rows'.
mrg.Range("A" & myValue).EntireRow.Interior.Color = RGB(255, 255, 153) 'highlight a random row
End Sub
The current value you have for count is misleading. Say it is 20, and the rows extend to 200; you cannot use the value 20 to identify the occupied rows amongst the 200 (unless you created a map/array to store all of the occupied row-numbers, and then choose randomly from these).
You could still determine the CountA if this information is useful to you, but not as part of the random selection process. Better to just find where the last occupied cell is, using End(xlUp).
Now you know the range containing the occupied cells and can choose randomly between these rows. Repeat the generation of a random number until you discover a cell/row that is not empty.
Sub RandomRow()
Dim wsMerged As Worksheet
Dim myRange As Range
Dim count As Long
Dim randRow As Long
Dim occupied As Boolean
Set wsMerged = Worksheets("Merged")
wsMerged.Range("A6:K200000").Interior.Color = RGB(255, 255, 255) 'reset cell colours
Set myRange = wsMerged.Columns("A:A")
'count = Application.WorksheetFunction.CountA(myRange)
count = Range("A200000").End(xlUp).Row - 6
Randomize 'necessary when using Rnd()
Do
randRow = CLng((count * Rnd()) + 6)
'if the cell isn't empty
If wsMerged.Range("A" & randRow).Value <> "" Then
'flag that we are done
occupied = True
wsMerged.Range("A" & randRow).Resize(1, 11).Interior.Color = RGB(255, 255, 153)
End If
Loop While Not occupied
End Sub
To clear cell colours I would set .Interior.ColorIndex = xlColorIndexNone.
At first MyValue is not declared.
Dim MyValue as Long
Now, its better. Secondly, your macro highlight all row, but clear colors only from A to K column. You would rather highlight first 11 columns, wouldn't you?
mrg.Range("A" & myValue).Resize(1, 11).Interior.Color = RGB(255, 255, 153)
And, to answer your question, set myRange like that:
Set myRange = mrg.Range("A6:A200000")
Then your Count worksheet function will not count headings actually. I hope, this helped.
Related
I'm working on an assignment that requires the solution use VBA, so I can't use indexing or other options on the worksheet page... I've looked all over for an answer, maybe I'm just asking the question incorrectly.
In column K there are ticker symbols, i.e. A, ABM, etc.
In column L there is an number (I've been classifying as Long)
I want to put the highest number in column L in Range("O2") and the tag that is one column to the left in Range("N2").
I've found numerous ways to identify the high number in column L, but can not figure out how to return the adjacent cells value...
Here is the most recent code that I've been trying which is not working. When I remove the tag references the code runs fine, but I need the adjacent value too.
Thanks
Sub attempt38()
Dim sheet As Worksheet
Dim i As Long
Dim firstRow As Integer
Dim columnNumber As Integer
Dim max As Long
Dim tag As Long
firstRow = 2
columnNumber = 12
Set sheet = ActiveSheet
If sheet.UsedRange.Rows.Count <= 1 Then max = 0 Else max = sheet.Cells(2, 12)
For i = firstRow To 300
If sheet.Cells(i, 12) > max Then max = sheet.Cells(i, 12) & (tag = sheet.Cells(i, 11))
Next
sheet.Cells(3, 14) = max
sheet.Cells(4, 14).Value = tag
End Sub
You don't need VBA. You can just use regular excel.
=LARGE(L:L,1)
Will return the largest number.
To get the corresponding just use Index plus match.
=INDEX(K:K,MATCH(LARGE(L:L,1),L:L,FALSE),1)
If you really want to use VBA, adjust your code to be two lines like so:
For i = firstRow To 300
If sheet.Cells(i, 12) > max Then
max = sheet.Cells(i, 12)
tag = sheet.Cells(i, 11)
Endif
Next
Or if you want to look sophisticated:
For i = firstRow To 300
With sheet.Cells(i, 12)
If .Value > max Then
max = .Value
tag = .Offset(0,-1).Value
Endif
End With
Next i
Looping through a range can be time consuming and, in this case, also wasteful.
What if your max value actually exists in the first looped row? You will now loop through 299 rows for nothing.
The below method will be much faster and requires no loops.
Option Explicit
Sub Mad_Max()
Dim ws As Worksheet: Set ws = ThisWorkbook.Sheets("Sheet1")
Dim MyMax As Long, MaxCell As Range
MyMax = Application.WorksheetFunction.Max(ws.Range("L:L"))
Set MaxCell = ws.Range("L:L").Find(MyMax, Lookat:=xlWhole)
ws.Range("N3") = MyMax
ws.Range("N4") = MaxCell.Offset(, -1)
End Sub
I have a spreadsheet that keeps track of project hours. Each project is assigned a color. A row contains half hour increments. Each half hour gets assigned a color based on which project is being worked on during that time.
I want to count the number of changes from one color to another for the length of the row. A row is one day.
How can I do this in Excel?
Below is what a row looks like. Each row is one day. I need to count how many times the color changed in this row left to right. So the output in this case would be 7 because the project changed 7 times.
This is a slightly more condensed UDF:
Function CountColorChanges(rng As Range) As Integer
Dim i As Integer
With rng.Columns
If .Count > 1 Then
For i = 1 To .Count - 1
If rng.Cells(1, i + 1).Interior.Color <> rng.Cells(1, i).Interior.Color Then CountColorChanges = CountColorChanges + 1
Next i
End If
End With
End Function
Option Explicit
Function NumberOfColorChanges(ByVal rng As range)as long
Dim cell As range
Dim color As Long
Dim firstCell As Boolean
firstCell = True
For Each cell In rng
If firstCell = True Then
color = cell.Interior.color
firstCell = False
Else
If color <> cell.Interior.color Then
NumberOfColorChanges = NumberOfColorChanges + 1
color = cell.Interior.color
End If
End If
Next cell
End Function
I need to hide rows in excel based on the value of multiple cells in the same row. If my row contains all 0's or is blank I need it hid. If there is any integer (not 0 or neg) I need the row shown. On the same sheet I have 'section headers' that separate sections, and below that, a row of blanks. Can I leave those intentional blanks in? I've had a few partially working lines written out, I just can't get it all together.
Hope this make sense and thanks for any help!
Edit: Now if I have a hidden column I'd like for the 'sum' to disregard the hidden column. I've tried nesting something inside RowanC's answer but no go.
For Each myRow In hideRange.Rows
For Each cell In hideRange.Cells
If cell.Columns.Hidden = False Then
Total = Total + cell.Value
End If
Next
If Total = 0 Then 'if the sum of the row=0 then hide
myRow.EntireRow.Hidden = True
End If
Next
My quick go at it:
Sub rowHider1()
Dim hideRange As Range
Dim myRow As Range
Set hideRange = Sheets(2).Range("A2:D12") 'you must set this to apply to the range you want (you could use active selection if you wanted)
hideRange.Rows.EntireRow.Hidden = False 'unhide any rows currently hidden
For Each myRow In hideRange.Rows
If Application.WorksheetFunction.Sum(myRow) = 0 Then 'if the sum of the row=0 then hide
myRow.EntireRow.Hidden = True
End If
Next
End Sub
to take this a step further, and if a column is hidden, don't include it in the sum, moving away from worksheetFunction sum:
Sub rowHider()
Dim hideRange As Range
Dim myRow As Range
Dim cell As Range
Dim bob As Double
Set hideRange = Sheets(2).Range("A1:G12") 'you must set this to apply to the range you want (you could use active selection if you wanted)
hideRange.Rows.EntireRow.Hidden = False 'unhide any rows currently hidden
For Each myRow In hideRange.Rows
bob = 0
For Each cell In myRow.Cells()
If cell.EntireColumn.Hidden <> True Then
Debug.Print cell.Address & " " & cell.Value
bob = bob + cell.Value
End If
Next
If bob = 0 Then 'if the sum of the row=0 then hide
myRow.EntireRow.Hidden = True
End If
Next
End Sub
I'm trying to write a macro to change the colors of rows when the values in column B change. Column A will be my controlling column using 1's and 0's, i.e. column A will stay a 1 as long as column B stays the same; whenever B changes, A will flip to a 0, and so on.
I can get it to color the rows correctly when the values in column B change, but the problem arises when I filter the data. For example: let's say I have B2-B4 set to "test1", B5-B7 set to "test2", and B8-B10 set to "test3", then I filter column B to not include "test2". Originally, the rows would be colored differently where the column values changed, but rows B2-B4 and B8-B10 are set to the same color and now they're touching since the "test2" rows are hidden.
Here's the code I used to color the rows, but it doesn't work for filtering:
Sub ColorRows()
Dim This As Long
Dim Previous As Long
Dim LastRow As Long
Dim Color As Integer
Dim R As Long
LastRow = ActiveSheet.Range("A" & Rows.Count).End(xlUp).Row
RwColor = Array(15,0)
Color = 0
For R = 2 To LastRow
This = Cells(R, 1).Value
Previous = Cells(R - 1, 1).Value
If This <> Previous Then Color = 1 - Color
Range("A" & R & ":M" & R).Select
Selection.Interior.ColorIndex = RwColor(Color)
Next R
End Sub
How can I fix it so that even after filtering the rows are colored correctly when there is a change in column values?
Here's a way to do this:
1.) Insert the code below as a UDF in a code module.
2.) Then put the formula in A, as A2: =analyseVisible(B2).
This will compare B-cells to the next visible cell above and result in a 'rank'-counter in A.
Now that the counter in A in contiunous (even if rows are hidden), you can use MOD 2 to color it with conditional formatting:
3.) Add a conditional format (from A2 for the whole table): =MOD($A2,2)=1 and set the fill color.
If you use the filter now or change values in B, the rows are re-colored in realtime.
Public Function analyseVisible(r As Range) As Integer
Dim i As Long
If Application.Caller.Row <= 2 Or _
r.Row <> Application.Caller.Row Then
analyseVisible = 1
Exit Function
End If
i = r.Row - 1
While r.Worksheet.Rows(i).Hidden And i > 1
i = i - 1
Wend
If i = 1 Then
analyseVisible = 1
Else
analyseVisible = r.Worksheet.Cells(i, Application.Caller.Column).Value
If r.Worksheet.Cells(i, r.Column).Value <> _
r.Value Then analyseVisible = analyseVisible + 1
End If
End Function
The code below handles the issue by checking only the used & visible rows. It works pretty well, but I was unable to figure out how to fire it when the filter changes. It also does it's comparisons directly on the values that are changing.
Private Sub colorRows()
Dim this As Variant
Dim previous As Variant
Dim currentColor As Long
Dim rng As Range 'visible range
Dim c As Range ' cell
' pick a color to start with
currentColor = vbYellow
' rng = used and visible cells
Set rng = ActiveSheet.UsedRange.SpecialCells(xlCellTypeVisible)
For Each c In rng ' For each cell that is visible and used
If Not c.Row = 1 Then ' skip header row
this = c.Value
'some simple test logic to switch colors
If this <> previous Then
If currentColor = vbBlue Then
currentColor = vbYellow
ElseIf currentColor = vbYellow Then
currentColor = vbBlue
End If
End If
'set interior color
c.Interior.color = currentColor
previous = this
End If
Next c
End Sub
Then, in the module of the worksheet that you want to colorize, call the sub from the Worksheet_Activate() event. (In reality, you probably want a different event. I mostly work with Access, so I don't really know what's available to you. I'm just trying to point you in the right direction to what I'm sure is your next question if you stick with the method you started with.)
Private Sub Worksheet_Activate()
colorRows
End Sub
I have the code form another post here, but I cannot seem to get it to actually highlight individual cell differences in each row. I have a current sheet and a previous sheet; the idea is that the code should compare serial numbers in one column (the same on both worksheets) and do two things:
1) If a value appears on the Current sheet, but is not on the Previous, then the entire row on the Current sheet is highlighted Green. (This work with the current code); and
2) If a matching value is on both sheets then the rows should be compared and any value on the Current sheet that is different from the Previous is highlighted yellow. (This does not work)
The number and order of columns is always the same. The serial numbers do not change and are unique to each entry. The code that I have been looking at is:
Sub NewUpdates()
Const ID_COL As Integer = 31 'ID is in this column
Const NUM_COLS As Integer = 32 'how many columns are being compared?
Dim shtNew As Excel.Worksheet, shtOld As Excel.Worksheet
Dim rwNew As Range, rwOld As Range, f As Range
Dim x As Integer, Id
Dim valOld, valNew
Set shtNew = ActiveWorkbook.Sheets("CurrentList")
Set shtOld = ActiveWorkbook.Sheets("PreviousList")
Set rwNew = shtNew.Rows(5) 'first entry on "current" sheet
Do While rwNew.Cells(ID_COL).Value <> ""
Id = rwNew.Cells(ID_COL).Value
Set f = shtOld.UsedRange.Columns(ID_COL).Find(Id, , xlValues, xlWhole)
If Not f Is Nothing Then
Set rwOld = f.EntireRow
For x = 1 To NUM_COLS
If rwNew.Cells(x).Value <> rwOld.Cells(x).Value Then
rwNew.Cells.Interior.Color = vbYellow
Else
rwNew.Cells.Interior.ColorIndex = xlNone
End If
Next x
Else
rwNew.EntireRow.Interior.Color = vbGreen 'new entry
End If
Set rwNew = rwNew.Offset(1, 0) 'next row to compare
Loop
End Sub
I did not change much of anything in the coding itself, but the original discussion that I pulled this from did not continue any further. Any ideas on updating so that I can get it highlighting the individual cells to show differences?
edit: Found the link where Tim Williams responded to a similar question and I found this code. It can be found here.
If you change the part where you change the color to yellow to this (note the additional '(x)'), it should work:
For x = 1 To NUM_COLS
If rwNew.Cells(x).Value <> rwOld.Cells(x).Value Then
rwNew.Cells(x).Interior.Color = vbYellow
Else
rwNew.Cells(x).Interior.ColorIndex = xlNone
End If
Next x