Delete hidden grouped rows below selected summary row - excel

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

Related

How to match columns and count the matches using vba

I am working on one scenario where I have two sheets. Sheet1 is the master sheet and sheet2 which I am creating.
Column1 of Sheet1 is Object which has duplicate objects as well. So, what I have done is I have created a macro which will produce the unique Objects and will paste it in sheet2.
Now, from Sheet2, each of the objects should be matched with Sheet1 column1 and based on the matching results, it should also count the corresponding entries from other columns in sheet1 to sheet2.
Below are the snapshots of my two sheets
Sheet1
Sheet2
here is my macro code which will first copy and paste the unique objects from sheet1 to sheet2 Column1.
Sub UniqueObj()
Dim Sh1 As Worksheet
Dim Rng As Range
Dim Sh2 As Worksheet
Set Sh1 = Worksheets("Sheet1")
Set Rng = Sh1.Range("A1:A" & Sh1.Range("A65536").End(xlUp).Row)
Set Sh2 = Worksheets("Sheet1")
Rng.Cells(1, 1).Copy Sh2.Cells(1, 1)
Rng.AdvancedFilter Action:=xlFilterCopy, CopyToRange:=Sh2.Range("A1"), Unique:=True
End Sub
But, I am unable to move forward from there. I am pretty new and any help would be very greatful.
Thanks
If I'm understanding what you want correctly, you're just counting matching columns from Sheet1 where the value in the corresponding column isn't blank? If so this should do the trick.
Option Explicit
Sub GetStuffFromSheet1()
Dim ws1 As Worksheet, ws2 As Worksheet
Dim lastRow1 As Long, lastRow2 As Long
Dim x As Long
'turn on error handling
On Error GoTo error_handler
Set ws1 = ThisWorkbook.Sheets("Sheet1")
Set ws2 = ThisWorkbook.Sheets("Sheet2")
'determine last row with data in sheet 1
lastRow1 = ws1.Cells(ws1.Rows.Count, 1).End(xlUp).Row
'determine last row with data in sheet 2
lastRow2 = ws2.Cells(ws2.Rows.Count, 1).End(xlUp).Row
'define columns in sheet 1
Const objCol1 As Long = 1
Const rProdCol1 As Long = 3
Const keysCol1 As Long = 4
Const addKeysCol1 As Long = 5
'define columns in sheet 2
Const objCol2 As Long = 1
Const rProdCol2 As Long = 2
Const keysCol2 As Long = 3
Const addKeysCol2 As Long = 4
'turn off screen updating + calculation for speed
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
'loop through all rows of sheet 2
For x = 2 To lastRow2
'formula counts # of cells with matching obj where value isn't blank
ws2.Cells(x, rProdCol2) = WorksheetFunction.CountIfs(ws1.Columns(objCol1), ws2.Cells(x, objCol2), ws1.Columns(rProdCol1), "<>" & "")
ws2.Cells(x, keysCol2) = WorksheetFunction.CountIfs(ws1.Columns(objCol1), ws2.Cells(x, objCol2), ws1.Columns(keysCol1), "<>" & "")
ws2.Cells(x, addKeysCol2) = WorksheetFunction.CountIfs(ws1.Columns(objCol1), ws2.Cells(x, objCol2), ws1.Columns(addKeysCol1), "<>" & "")
Next x
'turn screen updating + calculation back on
Application.ScreenUpdating = True
Application.Calculation = xlCalculationAutomatic
Exit Sub
error_handler:
'display error message
MsgBox "Error # " & Err.Number & " - " & Err.Description, vbCritical, "Error"
'turn screen updating + calculation back on
Application.ScreenUpdating = True
Application.Calculation = xlCalculationAutomatic
Exit Sub
End Sub
In case a non VBA solution works for you, you can resume your data with a Pivot Table, take field Object into rows section and rest of fields into values section (choose Count)
This returns the exact output you are looking for. Easy to update and easy to create.
In case you want a VBA solution, because your design is tabular and you are counting values, you can use CONSOLIDATE:
Consolidate data in multiple worksheets
'change K1 with cell where to paste data.
Range("K1").Consolidate Range("A1").CurrentRegion.Address(True, True, xlR1C1, True), xlCount, True, True, False
'we delete column relation type and column value. This columns depends on where you paste data, in this case, K1
Range("L:L,P:P").Delete Shift:=xlToLeft
After executing code i get this:
Hope this helps

Deleting Duplicates while ignoring blank cells in VBA

I have some code in VBA that is attempting to delete duplicate transaction IDs. However, i'd like to ammend the code to only delete duplicates that have a transaction ID - so, if there is no transaction ID, i'd like that row to be left alone. Here is my code below:
With MySheet
newLastRow = .Range("A" & .Rows.Count).End(xlUp).Row
newLastCol = .Cells(5 & .Columns.Count).End(xlToLeft).Column
Set Newrange = .Range(.Cells(5, 1), .Cells(newLastRow, newLastCol))
Newrange.RemoveDuplicates Columns:=32, Header:= _
xlYes
End With
I was also wondering - in the remove.duplicates command - is there a way where I can have the column I want looked at to be named rather than have it be 32 in case I add or remove columns at a later date?
Here is an image of the data: I'd like the ExchTransID column that have those 3 blank spaces to be left alone.
Modify and try the below:
Option Explicit
Sub test()
Dim Lastrow As Long, Times As Long, i As Long
Dim rng As Range
Dim str As String
'Indicate the sheet your want to work with
With ThisWorkbook.Worksheets("Sheet1")
'Find the last row with IDs
Lastrow = .Cells(.Rows.Count, "A").End(xlUp).Row
'Set the range with all IDS
Set rng = .Range("A1:A" & Lastrow)
'Loop column from buttom to top
For i = Lastrow To 1 Step -1
str = .Range("A" & i).Value
If str <> "" Then
Times = Application.WorksheetFunction.CountIf(rng, str)
If Times > 1 Then
.Rows(i).EntireRow.Delete
End If
End If
Next i
End With
End Sub

VBA Renaming sheets based on varible in a for loop and storing new variables

I'm trying to do the following tasks.
Create X-amount of new sheets in DestWorkbook based on row numbers in the Insert_Data_Sheet table. I've solved this
Rename the sheet according to the D-Column data starting from "D2". So I would like to to rename the first sheet "1865727" and the second sheet "1872188" etc. I've solved this
Store the data in D-column in a seperate variables. No luck with this yet
Here is pictures of the data:
https://pasteboard.co/HABwijq.jpg
https://pasteboard.co/HABwEhE.jpg
Full Code:
Public Sub TermSwap()
Application.ScreenUpdating = False
Dim DestWorkbook As Workbook, AC_Live_Workbook As Workbook, AC_Maturity_Workbook As Workbook
Dim Insert_Data_Sheet As Worksheet, AC_Live_Sheet As Worksheet, AC_Maturity_Sheet As Worksheet, Booked_Sheet As Worksheet
Dim i As Long, d As Long, lastRowA_AC_Live As Long, lastRow_AC_Maturity As Long, NumberOfPages As Long
'Dim Swap_Link_Tid As Long
'I will use these in the end when importing the AC Reports
'AC_Live_Filename = Application.GetOpenFilename(, , "AVAA AC LIVE RAPORTTI")
'AC_Maturity_Filename = Application.GetOpenFilename(, , "AVAA AC MATURITY RAPORTTI")
'Insert filename from above lines as a parameter in the end
Set DestWorkbook = Workbooks("TermSwap")
Set AC_Live_Workbook = Workbooks.Open(FileName:="C:\Users\z000479\Desktop\Makrot\Term Swap makro\Harjoitustiedostot\ALL_COLUMNS_FI_180817.xlsx")
Set AC_Maturity_Workbook = Workbooks.Open(FileName:="C:\Users\z000479\Desktop\Makrot\Term Swap makro\Harjoitustiedostot\ALL_COLUMNS_FI_180820.xlsx")
Set Insert_Data_Sheet = DestWorkbook.Sheets("Insert_Data")
Set Booked_Sheet = DestWorkbook.Sheets("booked")
Set AC_Live_Sheet = AC_Live_Workbook.Sheets("Result")
Set AC_Maturity_Sheet = AC_Maturity_Workbook.Sheets("Result")
'Finds the last row in A-Column in the AC_Live_Sheet and AC_Maturity_Sheet
lastRow_AC_Live = AC_Live_Sheet.Cells(AC_Live_Sheet.Rows.Count, "A").End(xlUp).Row
lastRow_AC_Maturity = AC_Maturity_Sheet.Cells(AC_Maturity_Sheet.Rows.Count, "A").End(xlUp).Row
'Create X-amount of new sheets in DestWorkbook based on row numbers in the Insert_Data_Sheet table.SOLVED
' Rename the sheet according to the D-Column data starting from "D2". SOLVED
' Store the data in D-column in a seperate variables. UNSOLVED
NumberOfPages = Insert_Data_Sheet.Cells((Insert_Data_Sheet.Rows.Count), "A").End(xlUp).Row - 1
Dim target_range As String
For d = 2 To NumberOfPages + 1
target_range = Insert_Data_Sheet.Range("D" & d).Value
DestWorkbook.Worksheets.Add(After:=DestWorkbook.Worksheets(DestWorkbook.Worksheets.Count)).Name = target_range
Next d
' AC LIVE Starts here:
' Show all cells
If AC_Live_Sheet.FilterMode Then
AC_Live_Sheet.ShowAllData
End If
'Delete row 2
AC_Live_Sheet.Range("2:2").Delete
'Autofiter ON. Filters LIVE_DEAL and SWAP_LINK_TID. Change SWAP_LINK_TID to a variable.
'Range syntax here is Range ("$A$1:$DS$" & lastRow)
If Not AC_Live_Sheet.AutoFilterMode Then
AC_Live_Sheet.Range("A1").AutoFilter
AC_Live_Sheet.Range("$A$1:$DS$" & lastRow_AC_Live).AutoFilter Field:=1, Criteria1:= _
"LIVE_DEAL"
AC_Live_Sheet.Range("$A$1:$DS$" & lastRow_AC_Live).AutoFilter Field:=7, Criteria1:= _
"1889087"
End If
'Copy pastes visible cells to Booked_Sheet("A1")
With AC_Live_Sheet
.UsedRange.SpecialCells(xlCellTypeVisible).Copy Destination:=Booked_Sheet.Cells(1, 1)
End With
' AC_MATURITY starts here
' Show all cells
If AC_Maturity_Sheet.FilterMode Then
AC_Maturity_Sheet.ShowAllData
End If
'Delete row 2
AC_Maturity_Sheet.Range("2:2").Delete
'Autofiter ON. Filters LIVE_DEAL and SWAP_LINK_TID.
'Range syntax here is Range ("$A$1:$DS$" & lastRow)
'I need to change SWAP_LINK_TID to a variable
If Not AC_Maturity_Sheet.AutoFilterMode Then
AC_Maturity_Sheet.Range("A1").AutoFilter
AC_Maturity_Sheet.Range("$A$1:$DS$" & lastRow_AC_Maturity).AutoFilter Field:=1, Criteria1:= _
"LIVE_DEAL", Operator:=xlOr, Criteria2:="=MAT_DEAL"
AC_Maturity_Sheet.Range("$A$1:$DS$" & lastRow_AC_Maturity).AutoFilter Field:=7, Criteria1:= _
"1889087"
End If
'Copy pastes visible cells to Booked_Sheet("A1")
With AC_Maturity_Sheet
.UsedRange.SpecialCells(xlCellTypeVisible).Copy Destination:=Booked_Sheet.Cells(6, 1)
End With
'Closes AC Workbooks and activates the Booked_Sheet
' Error here. It asked the file to be saved. I want to ignore it.
AC_Live_Workbook.Close
AC_Maturity_Workbook.Close
Booked_Sheet.Activate
Application.ScreenUpdating = True
End Sub
The following is to show how you might load the unique column D numbers into a dictionary as its keys and loop that dictionary's keys to add your new sheets. You could do your filter in the same loop, again using the current key of the dictionary for filtering or use it later. This is not intended to be copy-paste-work but to show you the parts you could use.
Option Explicit
Public Sub test()
Dim valuesDict As Object, arr(), i As Long, lastRow As Long
Set valuesDict = CreateObject("Scripting.Dictionary")
With ThisWorkbook.Worksheets("Sheet1")
lastRow = .Cells(.Rows.Count, "D").End(xlUp).Row 'find last row of your numbers
Select Case lastRow
Case Is < 2
Exit Sub
Case 2 '< Load your number into an array
ReDim arr(1, 1)
arr(1, 1) = .Range("D2")
Case Else
arr = .Range("D2:D" & lastRow).Value
End Select
End With
For i = LBound(arr, 1) To UBound(arr, 1) 'Add unique values to the range
valuesDict(arr(i, 1)) = 1
Next
Dim key As Variant
For Each key In valuesDict.keys
If Not Evaluate("ISREF('" & key & "'!A1)") Then 'If sheet doesn't exist add it. Credit to #Rory for this method.
ThisWorkbook.Worksheets.Add
ActiveSheet.NAME = key
End If
Next key
'Other code.......
For Each key In valuesDict.keys
AC_Live_Sheet.Range("$A$1:$DS$" & lastRow_AC_Live).AutoFilter Field:=7, Criteria1:=key
Next key
'Other code
End Sub

Transferring Cell Values Between Worksheets | Str Looper

Intended Result
If a row in a table contains any of the listed strings in column L on Sheet1, Then copy the entire row from Sheet1 and paste the row into a duplicate table on Sheet2 (which would be blank at the beginning).
(UNINTERESTED, UNRELATED, UNDECIDED, etc...)
Then delete the entire row that was transferred from sheet 1.
After macro runs, the new transfers should not reset table on Sheet2, rather add rows on the pre-existing lines. This document would be utilized over months.
Variables
Sheet1 is named Pipeline_Input
Sheet2 is named Closed_Sheet
Sheet1 table is named tblData
Sheet2 table is named tblClosed
Images
Image 1 is the code with error
Image 2 is Sheet 1 with some picture explanation
Image 3 is Sheet 2 with some picture explanation
Current Result
Run-time error '1004':
Application-defined or object-defined error
Sub closedsheet()
Application.ScreenUpdating = False
Dim Pipeline_input As Worksheet 'where is the data copied from
Dim Closed_Sheet As Worksheet 'where is the data pasted to
Dim strPhase() As String
Dim i As Integer
Dim intPhaseMax As Integer
Dim lngLstRow As Long
Dim rngCell As Range
Dim finalrow As Integer
Dim lr As Long 'row counter
Dim Looper As Integer
intPhaseMax = 6
ReDim strPhase(1 To intPhaseMax)
strPhase(1) = "LOST"
strPhase(2) = "BAD"
strPhase(3) = "UNINTERESTED"
strPhase(4) = "UNRELATED"
strPhase(5) = "UNDECIDED"
strPhase(6) = "BUDGET"
'set variables
Set Pipeline_input = Sheet1
Set Closed_Sheet = Sheet2
lr = Range("A" & Rows.Count).End(xlUp).Row
For Looper = LBound(strPhase) To UBound(strPhase)
For i = lr To 6 Step -1
Next
If Not Sheet1.Range("L9:L300" & lngLstRow).Find(strPhase(Looper), lookat:=xlWhole) Is Nothing Then
Range(Cells(i, 1), Cells(i, 20)).Copy
Sheet2.Range("A" & Rows.Count).End(3)(2).PasteSpecial xlPasteValues
Range(Cells(i, 1), Cells(i, 20)).Delete
End If
Next
Sheet2.Select
Sheet2.columns.AutoFit
Application.CutCopyMode = False
Application.ScreenUpdating = True
End Sub
Okay, there were a plethora of issues with the code you posted, but I decided to help you out here - Notice a few things - There's no copying and pasting here - we're just transferring data.
Secondly, use easy to understand variables. lr and lngLastRow can't be distinguished from one another, so classify them by which worksheet you're getting that value from.
We create an array in one fell swoop here - Just declare a variant and place our values in. ARRAYS (TYPICALLY) START AT ZERO, NOT ONE, so our loop starts at 0 :). Again, this is what's known as best practice...
I swapped out Looper for j. Again, keep. it. simple!
EDIT: I tested this code out on a simulated workbook and it worked fine - should run into no issues for you either.
EDIT2: Also, always use Option Explicit!
Option Explicit
Sub closedsheet()
Application.ScreenUpdating = False
Dim Pipeline_Input As Worksheet 'source sheet
Dim Closed_Sheet As Worksheet 'destination sheet
Dim i As Long, j As Long, CSlastrow As Long, PIlastrow As Long
Dim strPhase As Variant
'Here we create our array
strPhase = Array("LOST", "BAD", "UNINTERESTED", "UNRELATED", "UNDECIDED", "BUDGET")
'Assign worksheets
Set Pipeline_Input = ActiveWorkbook.Worksheets("Pipeline_Input")
Set Closed_Sheet = ActiveWorkbook.Worksheets("Closed_Sheet")
PIlastrow = Pipeline_Input.Range("A" & Rows.Count).End(xlUp).Row
For j = 0 To UBound(strPhase)
For i = PIlastrow To 6 Step -1
If Pipeline_Input.Range("L" & i).Value = strPhase(j) Then
'Refresh lastrow value
CSlastrow = Closed_Sheet.Range("A" & Rows.Count).End(xlUp).Row
'Transfer data
Closed_Sheet.Range("A" & CSlastrow + 1 & ":S" & CSlastrow + 1).Value = _
Pipeline_Input.Range("A" & i & ":S" & i).Value
'Delete the line
Pipeline_Input.Range("A" & i & ":S" & i).EntireRow.Delete
End If
Next i
Next j
Closed_Sheet.Select
Closed_Sheet.Columns.AutoFit
Application.ScreenUpdating = True
End Sub

Copy Paste VBA Code Has Blank Rows

The below code searches, copies & pastes the found data into another worksheet. However, there are blanks when this is done in the pasted worksheet. Eg: Found "To Be Copied" in Cell A1 and copied the entire row to the specified worksheet. Found "To Be Copied" in A4 and copied the entire row to the specified worksheet. However, there are two blank rows in the pasted sheet between A1 and A4. Thanks for your help.
Sub Deleting()
Application.ScreenUpdating = False
Dim wsh As Worksheet, i As Long, Endr As Long, x1 As Worksheet, p As Long
Set wsh = ActiveSheet
Worksheets.Add(Before:=Worksheets("Original Sheet")).Name = "Skipped"
Set x1 = Worksheets("Skipped")
Worksheets("ABC").Activate
i = 2
Endr = wsh.Range("A" & wsh.Rows.Count).End(xlUp).Row
While i <= Endr
If Cells(i, "A") = "To Be Copied" Then
wsh.Rows(i).Copy
x1.Rows(i).PasteSpecial
p = p + 1
Endr = Endr + 1
End If
i = i + 1
Wend
End Sub
You need two counters: i for the source rows, j for the destination rows. You only increment j when a row is copied.
Your existing code needs either
A separate counter for the written row position (Cutter's point), or
Pasting to the last used row of "Skipped" using xlUp to find the last used cell
But better still would be copying the rows in a single shot using AutoFilter. Something like below
Sub Quicker()
Dim ws1 As Worksheet
Dim ws2 As Worksheet
Dim rng1 As Range
Application.ScreenUpdating = False
Set ws1 = Sheets("ABC")
Set ws2 = Worksheets.Add(Before:=Worksheets("Original Sheet"))
'in case Skipped exists
On Error Resume Next
ws2.Name = "Skipped"
On Error GoTo 0
ws1.AutoFilterMode = False
Set rng1 = ws1.Range(ws1.[a1], ws1.Cells(Rows.Count, "A").End(xlUp))
rng1.AutoFilter 1, "To Be Copied"
If rng1.SpecialCells(xlCellTypeVisible).Count > 1 Then
Set rng1 = rng1.Offset(1, 0).Resize(rng1.Rows.Count - 1)
rng1.EntireRow.Copy ws2.[a1]
End If
ws1.AutoFilterMode = False
MsgBox "Sheet " & ws2.Name & " updated"
End Sub

Resources