Delete row if the column contains text - vba

I known, this question has been asked thousands of times. But every time I picked up a solution appears error when i debug. (error 1004)
I work with a database with about 300000 lines, where more than half do not care. (I know that have filter, but wanted to erase to reduce the file and speed up the process).
Then if the column M has a keyword like "water", "beer" or "vodka" it will delete the row. I mean, don't need to be the exact word, just the keyword.
OBS: Row 1 it's a table title with the frozen line.
Thanks!

The following code works less than 4 seconds for processing your sample data on my machine:
Sub QuickDeleteRows()
Dim Sheet_Data As Worksheet, NewSheet_Data As Worksheet, Data As Range
Dim Sheet_Name As String, Text As String, Water As Long, Beer As Long, Vodka As Long
On Error GoTo Error_Handler
SpeedUp True
Set Sheet_Data = Sheets("SOVI")
Sheet_Name = Sheet_Data.Name
LastRow = Cells(Rows.Count, 1).End(xlUp).Row
ReDim Output(1 To LastRow - 1, 1 To 1) As Long
For i = 1 To LastRow - 1
Text = Cells(i + 1, 13)
Water = InStr(Text, "water")
Beer = InStr(Text, "beer")
Vodka = InStr(Text, "vodka")
If Water > 0 Or Beer > 0 Or Vodka > 0 Then Output(i, 1) = 1
Next
[S2].Resize(LastRow - 1, 1) = Output
LastColumn = Cells(2, Columns.Count).End(xlToLeft).Column
Set Data = Sheet_Data.Range(Cells(1, 1), Cells(LastRow, LastColumn))
Set NewSheet_Data = Sheets.Add(After:=Sheet_Data)
Data.AutoFilter Field:=19, Criteria1:="=1"
Data.Copy
With NewSheet_Data.Cells
.PasteSpecial xlPasteColumnWidths
.PasteSpecial xlPasteAll
.Cells(1, 1).Select
.Cells(1, 1).Copy
End With
Sheet_Data.Delete
NewSheet_Data.Name = Sheet_Name
NewSheet_Data.Columns(19).Clear
Safe_Exit:
SpeedUp False
Exit Sub
Error_Handler:
Resume Safe_Exit
End Sub
Sub SpeedUp(SpeedUpOn As Boolean)
With Application
If SpeedUpOn Then
.ScreenUpdating = False
.EnableEvents = False
.Calculation = xlCalculationManual
.DisplayStatusBar = False
.DisplayAlerts = False
Else
.ScreenUpdating = True
.EnableEvents = True
.Calculation = xlCalculationAutomatic
.DisplayStatusBar = True
.DisplayAlerts = True
End If
End With
End Sub

In the future, please post code you've tried first for the community to help you out. That being said, try this out:
Sub Test()
Dim x as Long
Dim i as Long
x = Sheets("SOVI").Range("M" & Rows.Count).End(xlUp).Row
For i = x to 2 Step -1
If InStr(1, Range("M" & i).Value, "water", vbTextCompare) Or InStr(1, Range("M" & i).Value, "beer", vbTextCompare) Or InStr(1, Range("M" & i).Value, "vodka", vbTextCompare) Then
Range("M" & i).entirerow.delete
End If
Next i
End Sub

I would use a slightly different approach, with the Like and with Select Case - this will give you more versatility in the future if you would want to expand it to more types of drinks.
Sub FindDrink()
Dim lRow As Long
Dim i As Long
Dim sht As Worksheet
' always set your sht, modify to your sheet name
Set sht = ThisWorkbook.Sheets("Sheet1")
lRow = sht.Cells(sht.Rows.Count, "M").End(xlUp).Row
For i = lRow To 2 Step -1
Select Case True
Case (sht.Cells(i, "M").Value Like "*beer*") Or (sht.Cells(i, "M").Value Like "*water*") Or (sht.Cells(i, "M").Value Like "*vodka*")
Range("M" & i).EntireRow.Delete
Case Else
' if you decide to do other things in the future for other values
End Select
Next i
End Sub

use excel built in filtering functions for the maximum speed
Autofilter
Option Explicit
Sub main()
Dim keysToErase As Variant, key As Variant
keysToErase = Array("water", "beer", "vodka") '<--| list your keywords to delete matching column "M" rows with
Application.DisplayAlerts = False '<--| prevent alerts dialog box from appearing at every rows deletion
With Workbooks("test").Worksheets("SOVI").Range("A1").CurrentRegion '<--| this gets the range of all contiguous cells to "A1"
For Each key In keysToErase '<--| loop through keys
.AutoFilter field:=13, Criteria1:="*" & key & "*" '<--| filter column "M" with key
If Application.WorksheetFunction.Subtotal(103, .Cells.Resize(, 1)) > 1 Then .Offset(1).Resize(.Rows.Count - 1).SpecialCells(xlCellTypeVisible).Delete '<--| delete filtered cells, if any
Next key
.Parent.ShowAllData '<--| .. show all rows back...
End With
Application.DisplayAlerts = True '<--| allow alerts dialog box back
End Sub
AdvancedFilter
Option Explicit
Sub main2()
Application.DisplayAlerts = False '<--| prevent alerts dialog box from appearing at every rows deletion
With Workbooks("test").Worksheets("SOVI").Range("A1").CurrentRegion '<--| this gets the range of all contiguous cells to "A1"
.AdvancedFilter Action:=xlFilterInPlace, CriteriaRange:=.Parent.Range("U1:U4") '<--| this filters on all keys you placed in cells "U2:U4" with cell "U1" with wanted data header
If Application.WorksheetFunction.Subtotal(103, .Cells.Resize(, 1)) > 1 Then .Offset(1).Resize(.Rows.Count - 1).SpecialCells(xlCellTypeVisible).Delete '<--| delete filtered cells, if any
.Parent.ShowAllData '<--| .. show all rows back...
End With
Application.DisplayAlerts = True '<--| allow alerts dialog box back
End Sub

Try with Below code
Sub test()
Application.DisplayAlerts = False
Dim lastrow As Long
Dim i As Long
Dim currentrng As Range
lastrow = Range("M" & Rows.Count).End(xlUp).Row
For i = lastrow To 2 Step -1
Set currentrng = Range("M" & i)
If ((currentrng Like "*water*") Or (currentrng Like "*beer*") Or (currentrng Like "*vodka*")) Then
currentrng.EntireRow.Delete shift:=xlUp
End If
Next i
Application.DisplayAlerts = True
End Sub

Related

Vba find duplicates and copy if none found

I'm trying to add a vba code that looks in a column on sheet YTDFigures and sees if there is a duplicate in sheet EeeDetails. If there isn't then I want to copy the YTDFigures data and paste in a new sheet.
The code I've tried gets an error run time error 91 on the line FinName = Worksheets("EeeDetails").Range("A:A").Find(What:=SearchName, LookIn:=xlValues) I thought this would work as if a match isn't found the .Find function returns nothing.
Sub CheckMatch()
Application.ScreenUpdating = False
Dim SearchName As Range, SearchNames As Range
Dim Usdrws As Long
Dim row As Integer
Usdrws = Worksheets("YTDFigures").Range("A" & Rows.Count).End(xlUp).row
Set SearchNames = Worksheets("YTDFigures").Range("A2:A" & Usdrws)
For Each SearchName In SearchNames
row = Split(SearchName.Address, "$")(2)
FinName = Worksheets("EeeDetails").Range("A:A").Find(What:=SearchName, LookIn:=xlValues)
If FinName Is Nothing Then
Range("A" & row & ":S" & row).Copy
LastRow = Worksheets("Errors").Range("AA" & Rows.Count).End(xlUp).row + 1
Worksheets("Errors").Activate
Range("A" & LastRow).Select
Selection.PasteSpecial
Worksheets("EeeDetails").Activate
End If
Next
Application.ScreenUpdating = True
End Sub
You can place the raw data into an array, place the array on a temporary sheet, remove the duplicates, copy the data, then delete the temp sheet.
See below:
Sub CheckMatch()
Application.ScreenUpdating = False
Dim ws As Worksheet, tRows As Long
Set ws = ThisWorkbook.Worksheets(1)
Set RngA = ws.UsedRange.Columns("A")
tRows = ws.Rows(ws.Rows.Count).End(xlUp).row
Dim valA As Variant
valA = ws.Range(ws.Cells(1, 1), ws.Cells(tRows, 1)).Value
Dim tempWs As Worksheet
Set tempWs = ThisWorkbook.Worksheets.Add
tempWs.Name = "Temp1"
With tempWs
.Range(.Cells(1, 1), .Cells(tRows, 1)) = valA
With .UsedRange.Columns("A")
.RemoveDuplicates Columns:=1, Header:=xlYes
.Copy
End With
End With
' Do what you need to do with your copied data
Application.DisplayAlerts = False
tempWs.Delete
Application.DisplayAlerts = True
Application.ScreenUpdating = True
End Sub
Edit:
I just tested this with sample data of over 10k rows, and it works in less than a half a second. It's very fast.

Optimizing VBA / Excel Macro Code (Finding Duplicates and Sorting Large Data Set )

I currently have a code written to find duplicate values starting in range "A3" to last row used; highlight duplicates red, both the first and last instance; Filter by color highlighted and finally sort smallest to largest.
I will be using these duplicates later to copy to another sheet. The data starts in column "A3" to "V3" and to last row used. Data will range anywhere from 10,000 to 40,000 rows, maybe more depending on the data received.
My problem is this marco runs very slow and at times will freeze up.. Is there another way to achieve the same result but more efficiently and quicker?
Sub filtersort ()
Dim sht As Worksheet
Set sht = Worksheets("Sheet1")
Lastrow = Range("A" & Rows.Count).End(xlUp).Row
N = Cells(Rows.Count, "A").End(xlUp).Row
sht.Range("A3:A" & Lastrow).Select
Selection.FormatConditions.AddUniqueValues
Selection.FormatConditions(Selection.FormatConditions.Count).SetFirstPriority
Selection.FormatConditions(1).DupeUnique = xlDuplicate
With Selection.FormatConditions(1).Font
.Color = -16383844
.TintAndShade = 0
End With
With Selection.FormatConditions(1).Interior
.PatternColorIndex = xlAutomatic
.Color = 13551615
.TintAndShade = 0
End With
Selection.FormatConditions(1).StopIfTrue = False
sht.Range("A3:A" & Lastrow).Select
Application.CutCopyMode = False
Selection.AutoFilter
ActiveSheet.Range("$A$3:$A$" & Lastrow).AutoFilter Field:=1, Criteria1:=RGB(255, _
199, 206), Operator:=xlFilterCellColor
sht.Range("A3:V" & N).Sort Key1:=Range("A1"), Order1:=xlDescending, Header:=xlYes
End Sub
The autofilter is responsible for slow running code. The number of unique items would all affect the speed of the code.
If your intention is to retrieve the sorted duplicate data, you may try this approach.
The code given below will add a new sheet called "Duplicate Data" with all the duplicate data and sort it on column A.
The code assumes that the data is on a sheet called Sheet1, row3 being the header row and actual data starts from row4.
Modify it if required.
Sub filtersort()
Dim wsData As Worksheet, wsOutput As Worksheet
Dim Rng As Range
Dim LastRow As Long, LastCol As Long, i As Long, j As Long, n As Long
Dim arr(), x, dict, arrOut()
With Application
.Calculation = xlCalculationManual
.EnableEvents = False
.ScreenUpdating = False
End With
Set wsData = Worksheets("Sheet1")
On Error Resume Next
Set wsOutput = Sheets("Duplicate Data")
wsOutput.Cells.Clear
On Error GoTo 0
If wsOutput Is Nothing Then
Sheets.Add(after:=wsData).Name = "Duplicate Data"
Set wsOutput = ActiveSheet
End If
LastRow = wsData.Range("A" & Rows.Count).End(xlUp).Row
LastCol = wsData.Cells(3, Columns.Count).End(xlToLeft).Column + 1
Set Rng = wsData.Range("A3:A" & LastRow)
x = wsData.Range("A4:V" & LastRow).Value
Set dict = CreateObject("Scripting.Dictionary")
For i = 1 To UBound(x, 1)
If Not dict.exists(x(i, 1)) Then
dict.Item(x(i, 1)) = ""
Else
j = j + 1
ReDim Preserve arr(1 To j)
arr(j) = x(i, 1)
End If
Next i
ReDim arrOut(1 To UBound(x, 1), 1 To UBound(x, 2))
For i = 1 To UBound(x, 1)
If Not IsError(Application.Match(x(i, 1), arr, 0)) Then
n = n + 1
For j = 1 To UBound(x, 2)
arrOut(n, j) = x(i, j)
Next j
End If
Next i
wsData.Range("A3:V3").Copy wsOutput.Range("A3")
wsOutput.Range("A4").Resize(n, UBound(x, 2)).Value = arrOut
LastRow = wsOutput.Cells(Rows.Count, 1).End(xlUp).Row
wsOutput.Range("A3:V" & LastRow).Sort Key1:=wsOutput.Range("A4"), Order1:=xlDescending, Header:=xlYes
With Application
.Calculation = xlCalculationAutomatic
.EnableEvents = True
.ScreenUpdating = True
End With
End Sub
You can use Pivot Table to bring Count of the items and just remove filter from blank and 1 count items and here is your duplicate values list. You can automate this process with using VBA.
Write a formula in the last column of the sheet that would return the RowNumber for the record. Meaning the first time a record is found it returns 1. The second time it returns 2, third time 3, etc.
Once you have this formula correct you can automate this part in vba.
Now sort your data by this column.
Cut and paste in bulk where rowNumber>1. So many times I see similar things where people process it line by line in vba. It is a lot slower then using formula in the workbook. Sorting and cutting.

if cell contains specific text, copy whole row

I'm trying to create a macro that does this:
Check the values from a small list (I've used an array)
Go in a worksheet and for every row that contains one of the values of the array copy the entire row in another worksheet.
I've mixed other macros to create one but I got one problem, the macro check the value on the array and copies all the rows in my worksheet but every time it doesn't copy the first row found: ex, if row that contain "abl" are: 100,200 and 300, the macro just copy 200 and 300 ignoring 100.
This is my macro
Sub Test_339_1()
With Application
.Calculation = xlCalculationManual
.ScreenUpdating = False
Dim nam(1 To 7) As String, cel As Range, rng As Range
i = 1
Set rng = Worksheets("Ctr 339").Range("V4:V10")
For Each cel In rng
nam(i) = cel.Value
i = i + 1
Next cel
For i = 1 To 7
For Each cell In Sheets("FB03").Range("E:E")
If cell.Value = nam(i) Then
matchRow = cell.Row
Rows(matchRow & ":" & matchRow).Copy
Sheets("Test_macro").Select
ActiveSheet.Rows(matchRow).Select
ActiveSheet.Paste
Sheets("FB03").Select
End If
Next
Sheets("Test_macro").Select
Next i
Sheets("Test_macro").Select
On Error Resume Next
Range("A1:A50000").Select
Selection.SpecialCells(xlCellTypeBlanks).EntireRow.Delete
.Calculation = xlCalculationAutomatic
.ScreenUpdating = True
End With
End Sub
Try this refactored code:
Sub Test_339_1()
Dim nam(1 To 7) As String, cel As Range, lastrow As Long
With Application
.Calculation = xlCalculationManual
.ScreenUpdating = False
nam = Worksheets("Ctr 339").Range("V4:V10").Value
lastrow = Sheets("FB03").Cells(Sheets("FB03").Rows.Count, "E").End(xlUp).Row
For Each cell In Sheets("FB03").Range("E1:E" & lastrow)
For i = 1 To 7
If cell.Value = nam(i) Then
matchRow = cell.Row
Sheets("FB03").Rows(matchRow).Copy Sheets("Test_macro").Rows(Sheets("Test_macro").Cells(Sheets("Test_macro").Rows.Count, "E").End(xlUp).Row + 1)
Exit For
End If
Next i
Next cell
.Calculation = xlCalculationAutomatic
.ScreenUpdating = True
End With
End Sub
It should be quicker, It will not loop over 7 million times.
AutoFilter() should speed things up quite a bit:
Option Explicit
Sub Test_339_1()
Dim nam As Variant
nam = Application.Transpose(Worksheets("Ctr 339").Range("V4:V10").Value)
With Sheets("FB03")
With .Range("E1", .Cells(.Rows.Count, "E").End(xlUp))
.AutoFilter Field:=1, Criteria1:=nam, Operator:=xlFilterValues '<--| filter referenced range on its 3rd column (i.e. "State") with 1
If Application.WorksheetFunction.Subtotal(103, .Cells) > 1 Then '<--| if any filterd cells other than header
With .Resize(.Rows.Count - 1).Offset(1).SpecialCells(xlCellTypeVisible)
.EntireRow.Copy Sheets("Test_macro").Cells(.Cells(1, 1).Row,1)
End With
End If
End With
.AutoFilterMode = False
End With
End Sub
you only need row 1 to be a header one, i.e. actual data to be filtered begin from row 2 downwards
also this pastes values in target sheet from cell A1 downwards without blank rows. Should original row sequence be respected, it can be done

Delete all rows if duplicate in excel - VBA

I need to remove all rows without leaving any unique record. If duplicate exists delete all matching rows. Criteria is column C if any duplicate record exists in column C then delete entire row (including unique).
Below given code is working but leaving the unique row Even I don't want that.
Code:
Sub DDup()
Sheets("MobileRecords").Activate
With ActiveSheet
Set Rng = Range("A1", Range("C1").End(xlDown))
Rng.RemoveDuplicates Columns:=Array(3, 3), Header:=xlYes
End With
End Sub
I like the code from Jeeped, but it isn't the best readable one. Therefore, here is another solution.
Sub remDup()
Dim rng As Range, dupRng As Range, lastrow As Long, ws As Worksheet
Dim col As Long, offset As Long, found As Boolean
'Disable all the stuff that is slowing down
Application.ScreenUpdating = False
Application.EnableEvents = False
Application.Calculation = xlCalculationManual
'Define your worksheet here
Set ws = Worksheets(1)
'Define your column and row offset here
col = 3
offset = 0
'Find first empty row
Set rng = ws.Cells(offset + 1, col)
lastrow = rng.EntireColumn.Find( _
What:="", After:=ws.Cells(offset + 1, col)).Row - 1
'Loop through list
While (rng.Row < lastrow)
Do
Set dupRng = ws.Range(ws.Cells(rng.Row + 1, col), ws.Cells(lastrow, col)).Find( _
What:=rng, LookAt:=xlWhole)
If (Not (dupRng Is Nothing)) Then
dupRng.EntireRow.Delete
lastrow = lastrow - 1
found = True
If (lastrow = rng.Row) Then Exit Do
Else
Exit Do
End If
Loop
Set rng = rng.offset(1, 0)
'Delete current row
If (found) Then
rng.offset(-1, 0).EntireRow.Delete
lastrow = lastrow - 1
End If
found = False
Wend
'Enable stuff again
Application.ScreenUpdating = True
Application.EnableEvents = True
Application.Calculation = xlCalculationAutomatic
End Sub
It works with more than one duplicate and you can define an row offset, which defines how much rows you ignore at the beginning of the column.
I like to try these without any declared variables. It is good practise for keeping your cell / worksheet / workbook hierarchy together.
Sub dupeNuke()
With Worksheets("Sheet1") '<~~ you should know what worksheet you are supposed to be on
If .AutoFilterMode Then .AutoFilterMode = False
With .Cells(1, 1).CurrentRegion
With .Resize(.Rows.Count - 1, 1).Offset(1, 2)
With .FormatConditions
.Delete
.Add Type:=xlExpression, Formula1:="=COUNTIF(C:C, C2)>1"
End With
With .FormatConditions(.FormatConditions.Count)
.Interior.Color = vbRed
End With
End With
With .Resize(.Rows.Count, 1).Offset(0, 2)
.AutoFilter Field:=1, Criteria1:=vbRed, Operator:=xlFilterCellColor
With .Resize(.Rows.Count - 1, .Columns.Count).Offset(1, 0)
If CBool(Application.Subtotal(103, Cells)) Then
.EntireRow.Delete
End If
End With
End With
With .Resize(.Rows.Count - 1, 1).Offset(1, 2)
With .FormatConditions
.Delete
End With
End With
End With
If .AutoFilterMode Then .AutoFilterMode = False
End With
End Sub
Obviously, this is heavily reliant on the With ... End With statement. An underrated / underused method in my estimation.

VBA: Extract Top 'x' Entries from each category

By way of simplified example, say you have the following dataset:
A B C
Name Group Amount
Dave A 2
Mike B 3
Adam C 4
Charlie A 2
Edward B 5
Fiona B 5
Georgie A 4
Harry C 1
Mary A 0
Delia A 0
Victor B 1
Dennis B 0
Erica A 4
Will B 4
I'm trying to extract the highest 'x' entries (let's say 2 in this example) from each group.
For example, the highest two entries in Group A are Georgie and Erica with 4. I also then want the highest two entries for Group B and C.
I want the VBA code to extract these rows and paste them on another worksheet for subsequent analysis.
I have tried code like this so far:
ActiveWorkbook.Worksheets("Sheet1").AutoFilter.Sort.SortFields.Add Key:=Range _
("C1"), SortOn:=xlSortOnValues, Order:=xlDescending, DataOption:= _
xlSortNormal
With ActiveWorkbook.Worksheets("Sheet1").AutoFilter.Sort
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
ActiveSheet.Range("$A$1:$C$15").AutoFilter Field:=2, Criteria1:="A"
Range("A5:C6").Select
Selection.Copy
Sheets("Sheet2").Select
Range("A2").Select
ActiveSheet.Paste
Sheets("Sheet1").Select
ActiveSheet.Range("$A$1:$C$15").AutoFilter Field:=2, Criteria1:="B"
Range("A2:C3").Select
Application.CutCopyMode = False
Selection.Copy
Sheets("Sheet2").Select
Range("E2").Select
ActiveSheet.Paste
Sheets("Sheet1").Select
ActiveSheet.Range("$A$1:$C$15").AutoFilter Field:=2, Criteria1:="C"
Range("A4:C11").Select
Application.CutCopyMode = False
Selection.Copy
Sheets("Sheet2").Select
Range("I2").Select
ActiveSheet.Paste
In short, I'm just sorting the values from Largest to Smallest, and then filtering for each group, and extracting the top two values. The code is not resilient, however, as the copy part depends on the names being in a particular order, which will change when I get new data.
Is there a cleverer, cleaner way of doing this?
Does this have to be VBA? It can be done with formulas.
Based on your provided sample data, you could setup Sheet2 like this:
In cell A4 and copied down is this formula:
=IF($C4="","",INDEX(Sheet1!$A$2:$A$15,MATCH(1,INDEX((Sheet1!$B$2:$B$15=$B4)*(Sheet1!$C$2:$C$15=$C4)*(COUNTIFS($A$3:$A3,Sheet1!$A$2:$A$15,$B$3:$B3,$B4)=0),),0)))
In cell B4 and copied down is this formula:
=IF(($B$1>0)*COUNT($B$1),IF(OR($B3="Group",COUNTIF($B$3:$B3,$B3)=$B$1),IFERROR(INDEX(Sheet1!$B$2:$B$15,MATCH(0,INDEX(COUNTIF($B$3:$B3,Sheet1!$B$2:$B$15),),0)),""),$B3),"")
In cell C4 and copied down is this formula:
=IF(OR($B4="",COUNTIF(Sheet1!$B$2:$B$15,$B4)<COUNTIF($B$4:$B4,$B4)),"",LARGE(INDEX(Sheet1!$C$2:$C$15*(Sheet1!$B$2:$B$15=$B4),),COUNTIF($B$4:$B4,$B4)))
Note that you can copy those formulas down quite a ways, and it will only show desired results. Extra rows will simply be blank. You can also change the number in cell B1 to be whatever the number of top entries to be, so you could see top 5 per category, or top 3, etc.
However, if it absolutely must be VBA, then something like this should work for you. It's not simple, but it is very efficient and flexible. All you would need to do is update lNumTopEntries, your sheetnames, and where your data is located for the Set rngData line:
Sub tgr()
Dim wsData As Worksheet
Dim wsDest As Worksheet
Dim rngData As Range
Dim rngFound As Range
Dim rngUnqGroups As Range
Dim GroupCell As Range
Dim lCalc As XlCalculation
Dim aResults() As Variant
Dim aOriginal As Variant
Dim lNumTopEntries As Long
Dim i As Long, j As Long, k As Long
'Change to grab the top X number of entries per category'
lNumTopEntries = 2
Set wsData = ActiveWorkbook.Sheets("Sheet1") 'This is where your data is'
Set wsDest = ActiveWorkbook.Sheets("Sheet2") 'This is where you want to output it'
Set rngData = wsData.Range("A1", wsData.Cells(Rows.Count, "C").End(xlUp))
aOriginal = rngData.Value 'Store original values so you can set them back later'
'Turn off calculation, events, and screenupdating'
'This allows code to run faster and prevents "screen flickering"'
With Application
lCalc = .Calculation
.Calculation = xlCalculationManual
.EnableEvents = False
.ScreenUpdating = False
End With
'If there are any problems with the code, make sure the calculation, events, and screenupdating get turned back on'
On Error GoTo CleanExit
With rngData
.Sort .Resize(, 1).Offset(, 1), xlAscending, .Resize(, 1).Offset(, 2), , xlDescending, Header:=xlYes
End With
With rngData.Resize(, 1).Offset(, 1)
.AdvancedFilter xlFilterInPlace, , , True
Set rngUnqGroups = .Offset(1).Resize(.Rows.Count - 1).SpecialCells(xlCellTypeVisible)
.Parent.ShowAllData 'Remove the filter
ReDim aResults(1 To rngUnqGroups.Cells.Count * lNumTopEntries, 1 To 3)
i = 0
For Each GroupCell In rngUnqGroups
Set rngFound = .Find(GroupCell.Value, .Cells(.Cells.Count))
k = 0
If Not rngFound Is Nothing Then
For j = i + 1 To i + lNumTopEntries
If rngFound.Offset(j - i - 1).Value = GroupCell.Value Then
k = k + 1
aResults(j, 1) = rngFound.Offset(j - i - 1, -1).Value
aResults(j, 2) = rngFound.Offset(j - i - 1).Value
aResults(j, 3) = rngFound.Offset(j - i - 1, 1).Value
End If
Next j
i = i + k
End If
Next GroupCell
End With
'Output results'
wsDest.Range("A2").Resize(UBound(aResults, 1), UBound(aResults, 2)).Value = aResults
CleanExit:
'Turn calculation, events, and screenupdating back on'
With Application
.Calculation = lCalc
.EnableEvents = True
.ScreenUpdating = True
End With
If Err.Number <> 0 Then
'There was an error, show the error'
MsgBox Err.Description, , "Error: " & Err.Number
Err.Clear
End If
'Put data back the way it was
rngData.Value = aOriginal
End Sub
Something like this should work:
Sub TopValues()
Dim sht As Worksheet
Dim StartCell As Range
Set sht = Worksheets("Sheet1")
Set StartCell = Range("A1")
Set SrcRange = StartCell.CurrentRegion
Sheets.Add(After:=Sheets(Sheets.Count)).Name = "Data"
For i = 1 To 3
SrcRange.Sort Key1:=Worksheets("Sheet1").Range("A1").Offset(0, i - 1), Order1:=xlAscending, Header:=xlYes
sht.Rows("2:3").EntireRow.Copy
Worksheets("Data").Activate
ActiveSheet.Range("A" & 2 * i).PasteSpecial
Next i
End Sub
The Rows("2:3") and Range("A" & 2 * i) reflect your x value, which you said was 2 in this example. So the vba copies rows 2:3 and pastes them in row 2*i in the new sheet.