Deleting Hidden Rows in a Selected Range - excel

I want to delete all the hidden rows from a selected range. Here selected range is the range which user selects through the input box. In my loop I am moving from LastRow to StartRow.
I am facing problem defining the rows as I want the the StartRow to be the first row of that selected range and LastRow to be last row at the end of selected range's region
The code is giving a mismatch error '13'.
I am new to VBA and probably making some stupid mistake in the code below.
Sub Delete_Hidden_Row()
Dim LastRow As Long
Dim StartRow As Long
Dim r As Long
Dim MyRange As range
Set MyRange = Application.InputBox(Prompt:="Select the first Cell (Hidden Rows in the region of the
selected cell will be deleted) ", _
Title:="Delete Hidden Rows", Type:=8)
StartRow = range(MyRange.Rows, MyRange.Columns).Rows.EntireRow
LastRow = range(MyRange.Rows, MyRange.Columns).CurrentRegion.Rows.Count + 1
For r = LastRow To StartRow
If Rows(r).Hidden = True Then
Rows(r).Delete
End If
Next r

When deleting rows you need to adjust your loop slightly. If you delete the first row the second row becomes the first row and your upper limit becomes too big and so you get an error.
Easiest method to fix it is loop backwards, start from the last row and work backward.
Another method is to not increase your row counter until it encounters a visible row and moving on to the next.
Update
Below are examples of the methods I mentioned. When working with ranges the row is relative to the range, so it always goes from 1 to Row Count.
Sub Delete_Hidden_One()
Dim TargetRange As Range
Set TargetRange = Application.InputBox(Prompt:="Select the first Cell (Hidden Rows in the region of the selected cell will be deleted) ", Title:="Delete Hidden Rows", Type:=8)
Dim Row As Integer
Dim TargetStart As Integer
TargetStart = TargetRange.Rows.Count
For Row = TargetStart To 1 Step -1
If TargetRange.Rows(Row).EntireRow.Hidden = True Then
Debug.Print "Deleting Row <" & Row & ">"
TargetRange.Rows(Row).EntireRow.Delete xlShiftUp
Else
Debug.Print "Skipping Row <" & Row & ">"
End If
Next Row
End Sub
Sub Delete_Hidden_Two()
Dim TargetRange As Range
Set TargetRange = Application.InputBox(Prompt:="Select the first Cell (Hidden Rows in the region of the selected cell will be deleted) ", Title:="Delete Hidden Rows", Type:=8)
Dim Row As Integer
Row = 1
Do
If TargetRange.Rows(Row).EntireRow.Hidden = True Then
Debug.Print "Row <" & Row & "> Is Hidden - Deleting"
TargetRange.Rows(Row).EntireRow.Delete xlShiftUp
Else
If Row < TargetRange.Rows.Count Then
Debug.Print "Row <" & Row & "> Is Not Hidden - Incrementing"
Row = Row + 1
Else
Debug.Print "Row <" & Row & "> Is Out-Of-Bounds - Exiting"
Exit Do
End If
End If
Loop
End Sub

This should get you there:
StartRow = MyRange.Cells(1,1).Row
LastRow = MyRange.Cells(myRange.Rows.Count,1).Row
For r = LastRow To StartRow
If r.EntireRow.Hidden Then
Dim remove as Range
If remove Is Nothing Then
Set remove = r
Else
Set remove = Union(remove,r)
End If
End If
Next r
remove.EntireRow.Delete

Related

How do I Offset Rows correctly to Find the highest number in a Column in VBA?

I'm trying to run this code in VBA to find out which row has the highest number in column 'A'? But it's not working. Can someone help please? Below is the Code:
Sub ForNextDemo()
Dim MaxVal As Double
Dim Row As Long
MaxVal = WorksheetFunction.Max(Range("A:A"))
For Row = 1 To Rows.Count
If Range("A1").Offset(1, 0).Value = MaxVal Then
Range("A1").Offset(1, 0).Activate
MsgBox "Maximum Value is in" & Row
Exit For
End If
Next Row
End Sub
Your code fails because you check always the same cell. No matter which value row has, Range("A1").Offset(1, 0) will always check cell A2 (1 row below A1)
What you mean is probably something like Range("A1").Offset(row, 0)
However, there is a much easier (and faster) way to get the row with the maximum value, using the Match-function.
An advice: You should tell VBA always which sheet is should use. When you write Range(A1), it will use the current active sheet. This is not always what you want. Instead, use for example ThisWorkbook.Sheets(1) (first sheet of the workbook where the code is stored). You can also use the sheet name, eg ThisWorkbook.Sheets("Sheet1")
Dim MaxVal As Double
Dim Row As Long
With ThisWorkbook.Sheets(1)
Dim r As Range
Set r = .Range("A:A")
MaxVal = WorksheetFunction.max(r)
Row = WorksheetFunction.Match(MaxVal, r, 0)
Debug.Print "The maximum value is " & MaxVal & " and it is found in row " & Row
End With
Get the Maximum and the Row of Its First Occurrence
You can avoid the loop if there are no error values in the column.
Sub ForNextDemo()
Dim ws As Worksheet: Set ws = ActiveSheet ' improve!
Dim MaxVal As Variant ' could be an error value
Dim MaxRow As Long
With ws.Range("A:A")
If Application.Count(.Cells) = 0 Then
MsgBox "There are no numbers in the column.", vbCritical
Exit Sub
End If
MaxVal = Application.Max(.Cells)
If IsError(MaxVal) Then
MsgBox "There are error values in the column.", vbCritical
Exit Sub
End If
MaxRow = Application.Match(MaxVal, .Cells, 0)
' Select and scroll to the first 'max' cell.
Application.Goto .Cells(MaxRow), True
End With
MsgBox "The maximum value is " & MaxVal & "." & vbLf _
& "Its first occurrence is in row " & MaxRow & ".", vbInformation
End Sub

Delete hidden grouped rows below selected summary row

I use the below code in a macro to copy all rows from a "Template" sheet and paste them to the active sheet. Then all except the first of the pasted rows are grouped and "collapsed" i.e RowLevels:=1.
If .Outline.SummaryRow <> xlSummaryAbove Then .Outline.SummaryRow = xlSummaryAbove
csLastRow = copySheet.Cells(Rows.Count, 1).End(xlUp).Row
copySheet.Range("2:" & csLastRow).Copy
.Rows(LRow).PasteSpecial Paste:=xlPasteAll
.Range(.Cells(.Rows.Count, 1).End(xlUp), .Cells(.Rows.Count, 1).End _
(xlUp).Offset(-(csLastRow - 3), 1)).EntireRow.Group
ActiveSheet.Outline.ShowLevels RowLevels:=1, ColumnLevels:=0
This macro is run over and over to create a long list of summary rows with collapsed grouped rows below each as shown in the image.
The intention is to be able to delete a summary row and the collapsed group below it, if no longer needed on the sheet. As expected, when done manually, clicking on the summary row and deleting it, only deletes the summary row and appends the hidden rows below it to an adjacent group.
Is there a way to select the summary row and delete it along with the hidden grouped rows below it? How could I reference the first and last rows of a group in relation to the selected summary row above it in order to delete with vba?
Please, test the next code. Since your picture does not show the columns headers (if any of them is hidden), the code assumes that you want to qualify the group to be deleted according to its cell value in column "B:B" (see strCat value):
Sub DeleteSpecificGroup()
Dim ws As Worksheet, lastRow As Long, firstR As Long, cellC As Range
Dim strCat As String, i As Long, firsGRow As Long, lastGRow As Long
strCat = "Category 3" 'use there the category you need
Set ws = ActiveSheet 'use here the sheet you need
lastRow = ws.Range("A" & ws.rows.count).End(xlUp).row
Set cellC = ws.Range("B2:B" & lastRow).Find(What:=strCat, After:=ws.Range("B2"), _
LookIn:=xlValues, Lookat:=xlWhole)
If Not cellC Is Nothing Then
firsGRow = cellC.row 'first row of the group to be deleted
If ws.rows(cellC.row + 1).OutlineLevel > 1 Then
For i = cellC.row + 1 To lastRow
If ws.rows(i).EntireRow.ShowDetail Then
ws.rows(i).EntireRow.Hidden = False
Else
lastGRow = i - 1: Exit For 'last row of the group to be deleted
End If
Next i
End If
Else
MsgBox strCat & " could not be found in column ""B:B""...": Exit Sub
End If
ws.rows(firsGRow & ":" & lastGRow).EntireRow.Delete
End Sub
Edited:
To delete the group based on the group summary row selection, plese use the next code:
Sub DeleteSpecificSelectedGroup()
Dim ws As Worksheet, lastRow As Long, firstR As Long
Dim i As Long, firsGRow As Long, lastGRow As Long
Set ws = ActiveSheet 'use here the sheet you need
lastRow = ws.UsedRange.Rows.Count
If ws.Outline.SummaryRow <> xlSummaryAbove Then ws.Outline.SummaryRow = xlSummaryAbove
firsGRow = Selection.Row
Application.Calculation = xlCalculationManual
If ws.Rows(firsGRow + 1).OutlineLevel > 1 Then
For i = firsGRow + 1 To lastRow + 500
If ws.Rows(i).EntireRow.ShowDetail And ws.Rows(i).OutlineLevel > 1 Then
ws.Rows(i).EntireRow.Hidden = False
Else
lastGRow = i - 1: Exit For 'last row of the group to be deleted
End If
Next i
End If
ws.Rows(firsGRow & ":" & lastGRow).EntireRow.Delete
Application.Calculation = xlCalculationAutomatic
End Sub

Excel VBA: How do I add text to a blank cell in a specific column then loop to the next blank cell and add text?

I need a macro to add text to blank cells in Column A. The macro needs to skip cells that have text. The macro needs to stop looping at the end of the data set.
I am trying to use an If Else statement, but I think I'm on the wrong track. My current, non-working code is below. Thank you so much - I'm still new to VBA
Sub ElseIfi()
For i = 2 To 100
If Worksheets("RawPayrollDump").Cells(2, 1).Value = "" Then
Worksheets("RawPayrollDump").Cells(2, 1).Value = "Administration"
Else if(not(worksheets("RawPayrollDump").cells(2,1).value="")) then 'go to next cell
End If
Next
End Sub
To find the last row of data, use the End(xlUp) function.
Try this code. It replaces all empty cells in column A with Administration.
Sub ElseIfi()
Set ws = Worksheets("RawPayrollDump")
lastrow = ws.Cells(Rows.Count, 1).End(xlUp).Row ' last data row
For i = 2 To lastrow ' all rows until last data row
If ws.Cells(i, 1).Value = "" Then ' column A, check if blank
ws.Cells(i, 1).Value = "Administration" ' set text
End If
Next
End Sub
There is no need to loop. Please try this code.
Sub FillBlanks()
Dim Rng As Range
With Worksheets("RawPayrollDump")
Set Rng = Range(.Cells(2, "A"), .Cells(.Rows.Count, "A").End(xlUp))
End With
On Error Resume Next
Set Rng = Rng.SpecialCells(xlCellTypeBlanks)
If Err Then
MsgBox "There are no blank cells" & vbCr & _
"in the specified range.", _
vbInformation, "Range " & Rng.Address(0, 0)
Else
Rng.Value = "Administration"
End If
End Sub
Replace Blanks feat. CurrentRegion
Range.CurrentRegion
Since OP asked for "... stop looping at the end of the data set. ",
I've written this CurrentRegion version.
As I understand it, the end of the data set doesn't mean that there
cannot be blank cells below the last cell containing data in column
A.
Use the 1st Sub to test the 2nd, the main Sub (replaceBlanks).
Adjust the constants including the workbook (in the 1st Sub) to fit your needs.
Criteria is declared as Variant to allow other data types not just strings.
The Code
Option Explicit
Sub testReplaceBlanks()
Const wsName As String = "RawPayrollDump"
Const FirstCellAddress As String = "A2"
Const Criteria As Variant = "Administration"
Dim wb As Workbook: Set wb = ThisWorkbook
Dim ws As Worksheet: Set ws = wb.Worksheets(wsName)
replaceBlanks ws, FirstCellAddress, Criteria
End Sub
Sub replaceBlanks(Sheet As Worksheet, _
FirstCellAddress As String, _
Criteria As Variant)
' Define column range.
Dim ColumnRange As Range
Set ColumnRange = Intersect(Sheet.Range(FirstCellAddress).CurrentRegion, _
Sheet.Columns(Sheet.Range(FirstCellAddress) _
.Column))
' To remove the possibly included cells above the first cell:
Set ColumnRange = Sheet.Range(Range(FirstCellAddress), _
ColumnRange.Cells(ColumnRange.Cells.Count))
' Note that you can also use the addresses instead of the cell range
' objects in the previous line...
'Set ColumnRange = sheet.Range(FirstCellAddress, _
ColumnRange.Cells(ColumnRange.Cells.Count) _
.Address)
' or a mixture of them.
' Write values from column range to array.
Dim Data As Variant
If ColumnRange.Cells.Count > 1 Then
Data = ColumnRange.Value
Else
ReDim Data(1 To 1, 1 To 1): Data(1, 1) = ColumnRange.Value
End If
' Modify array.
Dim i As Long, k As Long
For i = 1 To UBound(Data)
If IsEmpty(Data(i, 1)) Then Data(i, 1) = Criteria: k = k + 1
Next i
' Write modified array to column range.
' The following line is used when only the first cell is known...
'Sheet.Range(FirstCellAddress).Resize(UBound(Data)).Value = Data
' ...but since the range is known and is the same size as the array,
' the following will do:
ColumnRange.Value = Data
' Inform user.
If k > 0 Then GoSub Success Else GoSub Fail
Exit Sub
' Subroutines
Success:
MsgBox "Wrote '" & Criteria & "' to " & k & " previously " _
& "empty cell(s) in range '" & ColumnRange.Address & "'.", _
vbInformation, "Success"
Return
Fail:
MsgBox "No empty cells in range '" & ColumnRange.Address & "'.", _
vbExclamation, "Nothing Written"
Return
End Sub

copy selected columns in a row to another sheet if a cell meets a condition

(not in a range, not adjacent columns)
(in given order)
I have many rows on Sheet1. I would like to copy some columns of a row (not the entire row and not a range of columns) to Sheet2 (to the first empty row of Sheet2) if a cell satisfies a condition (the cell in the current row and A column has a value of y)
I would like to copy not the entire row from Sheet1 only the row with those columns that are given on Sheet3 (Column A), and the new column number (on Sheet2) is also given on Sheet3 (column B)
It would be simple if my task would be to copy the entire row, or the selected column would be in a range...but i would need to copy those columns that are specialized on Sheet3. I would be grateful for any help. Thanks in advance.
Sheet1 shows an example data sheet. The criteria is if Cells(Rows, 1).Value = "y"
Sheet2 shows the desired result.
Sheet3 shows the selected column number on Sheet1 and the new column number on Sheet2
Whilst this probably should be done using arrays more, here's some basic VBA code that loops the first sheet checking for "y" in the first column. When it finds it, it then loops the column mappings in the third sheet that have been saved into arrays to set the values on the second sheet:
Sub sTranasferData()
On Error GoTo E_Handle
Dim aOld() As Variant
Dim aNew() As Variant
Dim wsIn As Worksheet
Dim wsOut As Worksheet
Dim wsTrack As Worksheet
Dim lngLastRow As Long
Dim lngLoop1 As Long
Dim lngLoop2 As Long
Dim lngRow As Long
Dim lngTrack As Long
Set wsIn = Worksheets("Sheet1")
Set wsOut = Worksheets("Sheet2")
Set wsTrack = Worksheets("Sheet3")
lngLastRow = wsIn.Cells(wsIn.Rows.Count, "A").End(xlUp).Row
lngTrack = wsTrack.Cells(wsTrack.Rows.Count, "A").End(xlUp).Row
aOld() = wsTrack.Range("A2:A" & lngTrack).Value
aNew() = wsTrack.Range("B2:B" & lngTrack).Value
lngRow = 1
For lngLoop1 = 2 To lngLastRow
If wsIn.Cells(lngLoop1, 1) = "y" Then
For lngLoop2 = LBound(aOld) To UBound(aOld)
wsOut.Cells(lngRow, aNew(lngLoop2, 1)) = wsIn.Cells(lngLoop1, aOld(lngLoop2, 1))
Next lngLoop2
lngRow = lngRow + 1
End If
Next lngLoop1
sExit:
On Error Resume Next
Set wsIn = Nothing
Set wsOut = Nothing
Set wsTrack = Nothing
Exit Sub
E_Handle:
MsgBox Err.Description & vbCrLf & vbCrLf & "sTransferData", vbOKOnly + vbCritical, "Error: " & Err.Number
Resume sExit
End Sub
Regards,

VBA: Find last row in a fixed number of ranges

I tried for a few hours to search and look for a possible answer. I am about ready to give up. I haven't been able to find someone with a scenario quite like the one I am asking, maybe I overlooked it.
I want to find the last row in a specific range. The ranges are A7 to A21. I want to be able to enter input data from my form to the empty row within that range...
Here is where it gets tricky. I also have two other categories on the same sheet where I need to input data. Data may already be here, again I want to find the last row and then input data. Ranges A27:A41.
And the last category ranges A46:A66.
Hopefully someone here can help me out.
Define the ranges you use as tables in Excel on the sheet. Then in your code use:
Dim Table1 As listObject, Table2 As ListObject
With ThisWorkbook.Worksheets("Name of the sheet the tables are on")
Set Table1 = .ListObjects("Name of the table")
Set Table2 = .ListObjects("Name of the table")
End With
Dim LastRowT1 As Long, LastRowT2 As Long
LastRowT1 = 1: LastRowT2 = 1
Do Until Table1.DataBodyRange(LastRowT1, 1) = Empty
LastRowT1 = LastRowT1 + 1
Loop
Do Until Table2.DataBodyRange(LastRowT2, 1) = Empty
LastRowT2 = LastRowT2 + 1
Loop
'If you run out of space and automatically want to add an extra row add
'the following code.
If LastRowT1 > Table1.ListRows.Count Then
Table2.ListRows.Add AlwaysInsert:=True
End If
If LastRowT2 > Table2.ListRows.Count Then
Table2.ListRows.Add AlwaysInsert:=True
End If
The Value of LastRowT1 and LastRowT2 should be the row number (of the listobject) of the first empty row.
This should get you pointed in the right direction...
Sub Main()
Dim r1 As Range
Dim r2 As Range
Dim r3 As Range
Dim rFind As Range
'Set your range vars
Set r1 = Range("A7:A21")
Set r2 = Range("A27:A41")
Set r3 = Range("A46:A66")
'Find the next empty cell and display the address
On Error Resume Next
'First range
Set rFind = r1.Find("*", searchdirection:=xlPrevious).Offset(1, 0)
If Not rFind Is Nothing Then
MsgBox "First open cell in " & r1.Address & " is " & rFind.Address
Else
MsgBox "First open cell in " & r1.Address & " is " & r1.Cells(1, 1).Address
End If
'Second range
Set rFind = r2.Find("*", searchdirection:=xlPrevious).Offset(1, 0)
If Not rFind Is Nothing Then
MsgBox "First open cell in " & r2.Address & " is " & rFind.Address
Else
MsgBox "First open cell in " & r2.Address & " is " & r2.Cells(1, 1).Address
End If
'Third range
Set rFind = r3.Find("*", searchdirection:=xlPrevious).Offset(1, 0)
If Not rFind Is Nothing Then
MsgBox "First open cell in " & r3.Address & " is " & rFind.Address
Else
MsgBox "First open cell in " & r3.Address & " is " & r3.Cells(1, 1).Address
End If
End Sub
This assumes that you're filling the cells from the top down (e.g. A7 fills first, A8 is next, then A9, etc.). If that's not the case, then instead of .Find you'd need to use a loop. You'll definitely need to adapt this to you situation, especially the logic for when all the cells in your ranges fill up.
To make your request more generic (and hence scalable), you could create a function to find the first available row of any given range:
Function FindFirstOpenCell(ByVal R As Range) As Integer
Dim row, col As Integer
row = R.row
col = R.Column
FindFirstOpenCell = Cells(row + R.Rows.Count - 1, col).End(xlUp).row + 1
End Function
From here you could simply call the function over and over:
Dim row As Integer
row = FindFirstOpenCell(Range("A7:A21"))
Cells(row, 1).Value = "My Next Item"

Resources