View Single Post
  #18   Report Post  
Posted to microsoft.public.excel.programming
Tasha Tasha is offline
external usenet poster
 
Posts: 157
Default Macro to delete rows

Bill, going to bug you again. Is there a way to remove the message box and
have it go ahead and delete the rows? I tested it and works, and will always
need it to delete, not mark, so if possible would like to have it just do it
instead of stopping in the middle of the macro.... I hate to ask after all
you did, but you help me with this???

"Bill Renaud" wrote:

Hi Tasha,

Here is some code that you can try (hope you are still checking the NG
every once in a while). I decided to write the routine, just to see what
was really involved in this type of situation. It is amazing how
something that appears relatively simple to a human can require several
(possibly hundreds of) lines of code to solve! I developed this with
Excel 2000, so hopefully it will run on whatever version you are using.

Just paste this code into a standard module in a new, empty workbook,
then attach a toolbar button to it. As always, watch for unwanted
word-wrap in the NG. Make sure that the worksheet with the data is the
active sheet when you start the macro. The macro will add 3 columns to
the right of your data, for sorting purposes, as well as marking the
rows that should be deleted. You will be prompted at the start for just
marking the rows, or marking and then automatically deleting them.

Check these results carefully to make sure it meets your needs!

'----------------------------------------------------------------------
'Global constants and variables
Const strMsgBoxTitle = "Delete Duplicate Rows"
Const conDELETE = "Delete" 'Constant to use to fill in Delete column.

Dim rngList As Range 'List of all data on the worksheet.

'----------------------------------------------------------------------
Public Sub DeleteDuplicateRows()
Dim varResponse As Variant 'vbYes or vbNo for DeleteMarkedRows.

Set rngList = ActiveSheet.UsedRange

If Not IsWorksheetValid Then GoTo ExitSub

varResponse = MsgBox("Press Yes to automatically mark" & vbNewLine _
& "and then delete duplicate rows." & vbNewLine _
& vbNewLine _
& "Press No to mark rows for deletion," & vbNewLine
_
& "but not automatically delete them.", _
vbExclamation + vbYesNo, _
strMsgBoxTitle)

Application.ScreenUpdating = False

'Add 3 columns at the right side of the data
'for sorting and processing purposes.
AppendHeaderCell conDELETE
AddOrderColumn
AddSortingColumn

FormatHeaderCells 'Format all column labels (headers).
MarkRowsForDeletion 'Mark rows to be deleted.

If varResponse = vbYes Then DeleteMarkedRows

SortList "Order" 'Re-sort data back to original order.

'Autofit columns for easier viewing.
rngList.Parent.Columns.AutoFit

ExitSub:
Application.ScreenUpdating = True
End Sub

'----------------------------------------------------------------------
Private Function IsWorksheetValid() As Boolean
Dim rngRoom As Range
Dim rngPatNo As Range
Dim rngPatName As Range
Dim rngCNSDay As Range
Dim rngAmt As Range

Dim rngDelete As Range
Dim rngOrder As Range
Dim rngSort As Range

'Check for column labels that SHOULD be present.
Set rngRoom = GetHeaderCell("ROOM")
Set rngPatNo = GetHeaderCell("PATNO")
Set rngPatName = GetHeaderCell("PATNAME")
Set rngCNSDay = GetHeaderCell("CNSDAY")
Set rngAmt = GetHeaderCell("AMT")

If (rngRoom Is Nothing) _
Or (rngPatNo Is Nothing) _
Or (rngPatName Is Nothing) _
Or (rngCNSDay Is Nothing) _
Or (rngAmt Is Nothing) _
Then
IsWorksheetValid = False
MsgBox "Worksheet is not a valid data set." & vbNewLine _
& vbNewLine _
& "Does not contain ""ROOM"", ""PATNO""," & vbNewLine _
& """PATNAME"", ""CNSDAY"", or ""AMT"" columns.", _
vbCritical + vbOKOnly, _
strMsgBoxTitle
GoTo ExitIsWorksheetValid
End If

'Check for column labels that should NOT be present.
Set rngDelete = GetHeaderCell(conDELETE)
Set rngOrder = GetHeaderCell("Order")
Set rngSort = GetHeaderCell("SortingColumn")

If Not (rngDelete Is Nothing) _
Or Not (rngOrder Is Nothing) _
Or Not (rngSort Is Nothing) _
Then
IsWorksheetValid = False
MsgBox "Worksheet has already been processed.", _
vbCritical + vbOKOnly, _
strMsgBoxTitle
GoTo ExitIsWorksheetValid
End If

'Check that there is least 1 row of data to process.
If (rngList.Rows.Count < 2) _
Then
IsWorksheetValid = False
MsgBox "No data to process.", _
vbCritical + vbOKOnly, _
strMsgBoxTitle
GoTo ExitIsWorksheetValid
End If

IsWorksheetValid = True

ExitIsWorksheetValid:
End Function

'----------------------------------------------------------------------
Private Sub AddOrderColumn()
Dim rngOrder As Range
Dim rngOrderData As Range

Set rngOrder = AppendHeaderCell("Order")
Set rngOrderData = GetDataArea(rngOrder)

'Put a value of 1 in the first cell.
rngOrderData.Cells(1, 1).Formula = 1#

'Now fill in the data series, sequentially by 1.
rngOrderData.DataSeries Rowcol:=xlColumns, _
Type:=xlLinear, _
Step:=1, _
Trend:=False
End Sub

'----------------------------------------------------------------------
Private Sub AddSortingColumn()
Dim rngSortingHeader As Range
Dim rngSortingData As Range

Set rngSortingHeader = AppendHeaderCell("SortingColumn")
Set rngSortingData = GetDataArea(rngSortingHeader)

'Add data to SortingColumn (sort by PATNAME, PATNO, CNSDAY, and ROOM).
rngSortingData.Formula = "=" & CellAddress("PATNAME", 1) _
& " & "" "" & " & CellAddress("PATNO", 1) _
& " & "" "" & " & CellAddress("CNSDAY", 1) _
& " & "" "" & " & CellAddress("ROOM", 1)
End Sub

'----------------------------------------------------------------------
Private Function AppendHeaderCell(strHeader As String) As Range

Dim rngNewHeaderCell As Range

'Add new column at the right of the list. Assume column is emtpy.
With rngList
Set rngNewHeaderCell = .Resize(1, 1) _
.Offset(ColumnOffset:=.Columns.Count)
End With

rngNewHeaderCell.Formula = strHeader

'Expand width of List to include the new column.
With rngList
Set rngList = .Resize(ColumnSize:=.Columns.Count + 1)
End With

Set AppendHeaderCell = rngNewHeaderCell
End Function

'----------------------------------------------------------------------
Private Sub FormatHeaderCells()
With rngList.Resize(RowSize:=1)
.Font.Bold = True
With .Borders(xlEdgeBottom)
.LineStyle = xlContinuous
.Weight = xlThin
End With
End With
End Sub

'----------------------------------------------------------------------
Private Function GetHeaderCell(strHeader As String) As Range

Dim rngHeaderCells As Range

Set rngHeaderCells = rngList.Resize(1)

Set GetHeaderCell = rngHeaderCells.Find(What:=strHeader, _
LookIn:=xlValues, _
LookAt:=xlPart)
End Function

'----------------------------------------------------------------------
Private Function GetDataArea(rngHeaderCell As Range) As Range

With rngHeaderCell
Set GetDataArea = .Offset(1, 0) _
.Resize(RowSize:=rngList.Rows.Count - 1)
End With
End Function

'----------------------------------------------------------------------
Private Function CellAddress(strHeaderCell As String, _
lngOffset As Long) As String

CellAddress = GetHeaderCell(strHeaderCell) _
.Offset(RowOffset:=lngOffset) _
.Address(RowAbsolute:=False, _
ColumnAbsolute:=False, _
ReferenceStyle:=xlA1)
End Function

'----------------------------------------------------------------------
Private Sub SortList(strHeaderCell As String)
Dim rngHeaderCell As Range

Set rngHeaderCell = GetHeaderCell(strHeaderCell)

rngList.Sort Key1:=rngHeaderCell, Order1:=xlAscending, _
Header:=xlYes, _
OrderCustom:=1, _
MatchCase:=False, _
Orientation:=xlTopToBottom
End Sub

'----------------------------------------------------------------------
Private Sub MarkRowsForDeletion()
Dim rngSort As Range 'Data area of SortingColumn.
Dim rngAmt As Range 'Data area of AMT column.
Dim rngDelete As Range 'Data area of Delete column.

Dim ilngFirst As Long 'Index to First record of a given patient.
Dim ilngLast As Long 'Index to Last record of a given patient.
Dim ilngEnd As Long 'Index to End record of all data.
Dim ilngCompare1 As Long 'Index to first record to compare.
Dim ilngCompare2 As Long 'Index to second record to compare.

'Sort data using the SortingColumn.
SortList "SortingColumn"

'Get references to data areas of
'"SortingColumn", "AMT", and "Delete" columns.
Set rngSort = GetDataArea(GetHeaderCell("SortingColumn"))
Set rngAmt = GetDataArea(GetHeaderCell("AMT"))
Set rngDelete = GetDataArea(GetHeaderCell(conDELETE))

'Initialize the loop.
ilngEnd = rngSort.Rows.Count
ilngLast = 0

'Loop to look for any records to be marked for deletion.
While (ilngLast < ilngEnd)
ilngFirst = ilngLast + 1
ilngLast = ilngFirst

'Find last row of data for this same
'patient-room combination etc.
While (ilngLast < ilngEnd)
If rngSort(ilngLast + 1) = rngSort(ilngLast) _
Then
ilngLast = ilngLast + 1
Else
GoTo CompareRecords
End If
Wend

CompareRecords:
'Compare all combinations or patient records that
'have not already been marked for deletion,
'then mark both for deletion.
If (ilngLast - ilngFirst) 0 _
Then
'There are at least 2 records, so they can be compared.
For ilngCompare1 = ilngFirst To ilngLast - 1
If rngDelete(ilngCompare1) < conDELETE _
Then
For ilngCompare2 = ilngCompare1 + 1 To ilngLast
If rngDelete(ilngCompare2) < conDELETE _
Then
If rngAmt(ilngCompare1) = -rngAmt(ilngCompare2) _
Then
'Mark both patient records for deletion.
rngDelete(ilngCompare1) = conDELETE
rngDelete(ilngCompare2) = conDELETE