sorting for worksheets in 1 workbook - vba

I have searched the net for a macro that can help me to do sorting for worksheets in a workbook and modified it a little ( adding the exclude worksheets)
Sub SortDataWorksheets()
Dim wsh As Worksheet
For Each wsh In ThisWorkbook.Sheets
If wsh.Name <> "Dashboard" And wsh.Name <> "rawdata" And wsh.Name <> "template" And wsh.Name <> "macros instructions" And wsh.Name <> "Sheet1" _
And wsh.Name <> "Sheet2" And wsh.Name <> "inputlist" And wsh.Name <> "ProductList" And wsh.Name <> "NA" Then
'sort columns A to AL based on data in column B
wsh.Columns("A:AL").Sort key1:=Range("B3"), order1:=xlAscending, Header:=xlYes
End If
Next
End Sub
However, this doesnt work as excel will throw the
Run Time error '1004' :
The sort reference is not valid. Make sure that it's within the data you want to sort...
My data starts from Row 3 onwards, 1st 2 rows are headers. How do i exclude the first 2 rows for sorting?

Change from:
wsh.Columns("A:AL").Sort key1:=Range("B3"), order1:=xlAscending, Header:=xlYes
To:
wsh.Columns("A:AL").Sort key1:=wsh.Range("B3"), order1:=xlAscending, Header:=xlYes
Because if you do not refer to the parent worksheet, VBA takes as parent worksheet the ActiveSheet or the sheet in which the code is. Both would return an error in your case.

This works for me:
Sub SortDataWorksheets()
Dim wsh As Worksheet
Dim LastRow As Long
For Each wsh In ThisWorkbook.Sheets
With wsh
If .Name <> "Dashboard" And .Name <> "rawdata" And .Name <> "template" And _
.Name <> "macros instructions" And .Name <> "Sheet1" _
And .Name <> "Sheet2" And .Name <> "inputlist" And _
.Name <> "ProductList" And .Name <> "NA" Then
LastRow = .Cells.Find(What:="*", SearchOrder:=xlRows, SearchDirection:=xlPrevious, LookIn:=xlValues).Row
'sort columns A to AL based on data in column B
.Range("A2:AL" & LastRow).Sort key1:=.Range("B3"), order1:=xlAscending, Header:=xlYes
End If
End With
Next
End Sub
Notice the use of a specific range instead of columns.

I find it easier to read a Select Case rather than multiple IF..AND..THEN when ignoring sheets.
The code below will adjust to how many rows contain data in column B.
I'm still not sure which is the preferred method of sorting - single line, or what the macro recorder returns (similar to below).
Public Sub SortDataWorksheets()
Dim wsh As Worksheet
Dim lLastRow As Long
For Each wsh In ThisWorkbook.Worksheets
Select Case wsh.Name
Case "Dashboard", "rawdata", "template", "macros instructions", _
"Sheet1a", "Sheet2a", "inputlist", "ProductList", "NA"
'Do nothing
Case Else
lLastRow = wsh.Cells(wsh.Rows.Count, 2).End(xlUp).Row
With wsh.Sort
With .SortFields
.Clear
.Add Key:=Range("B5:B" & lLastRow), _
SortOn:=xlSortOnValues, _
Order:=xlAscending, _
DataOption:=xlSortNormal
End With
.SetRange Range("A5:C" & lLastRow)
.Header = xlNo
.MatchCase = False
.Orientation = xlTopToBottom
'.SortMethod = xlPinYin 'Only need if sorting Chinese characters.
.Apply
End With
End Select
Next wsh
End Sub

Related

VBA Script to remove all lines other then my filtered values

I am currently in the process of creating a VBA Script where i extract a list of raw data and filter out values Apple, Banana, and Oranges. I then delete all the other rows if it is not the values mentioned above.
So for example i have apple, banana, orange, grape, mandarin, avocado, coconut, lemon, watermelon.
I only want to keep apple, banana and orange in the end. If it has any of the other fruits i want that whole row of information removed.
Sub RMWO_Clean()
Dim ws As Worksheet
Dim rng As Range
Dim lastRow As Long
Set ws = ActiveWorkbook.Sheets("Sheet1")
lastRow = ws.Range("Q" & ws.Rows.Count).End(xlUp).Row
Set rng = ws.Range("Q1:Q" & lastRow)
Columns("AF:AF").Select
Selection.TextToColumns Destination:=Range("AA1"), DataType:=xlDelimited, _
TextQualifier:=xlDoubleQuote, ConsecutiveDelimiter:=False, Tab:=True, _
Semicolon:=False, Comma:=False, Space:=False, Other:=False, FieldInfo _
:=Array(1, 1), TrailingMinusNumbers:=True
With rng
.AutoFilter Field:=1,Criteria1:="<>*Apple*", Operator:=xlAnd, Criteria2:="<>*Banana*"
.Offset(1, 0).SpecialCells(xlCellTypeVisible).EntireRow.Delete
End With
ws.AutoFilterMode = False
End Sub
I know that you cannot use
Criteria3:=xx
I have also tried
Criteria1:=Array("<>Apple", "<>Banana", "<>Orange")
But this seems to only leave orange behind.
Are you able to let me know what i am doing wrong?
Criteria1:=Array("<>Apple", "<>Banana", "<>Orange") needs Operator:=xlFilterValues operator, and yet won't work with those "<>"
so you can fool it by thinking the other way around:
filter "good" records
delete all records that are not good
like follows:
With rng
.AutoFilter Field:=1, Criteria1:=Array("Apple", "Banana", "Orange"), Operator:=xlFilterValues
With .Resize(.Rows.Count - 1, .Columns.Count).Offset(1, 0) ' reference 'records' only (skip headers)
Select Case Application.Subtotal(103, .Cells) ' count number of filtered cells
Case 0 'if no cells to save
.EntireRow.Delete ' delete all rows
Case Is < .Count 'if there's at least one row to delete
Set saveRng = .SpecialCells(xlCellTypeVisible) ' store cells to save
.Parent.AutoFilterMode = False 'remove filter
saveRng.EntireRow.Hidden = True 'hide cells to save
.SpecialCells(xlCellTypeVisible).EntireRow.Delete 'delete visible cells
saveRng.EntireRow.Hidden = False 'bring cells to save visible back
End Select
End With
.Parent.AutoFilterMode = False
End With
Starting with:
I run:
Dim myRange As Range
Set myRange = ActiveSheet.Range("$A$1:$A$4")
myRange.AutoFilter Field:=1, _
Criteria1:="<>*Banana*", Operator:=xlAnd, Criteria2:="<>*apple*"
...and I get:
...and then I run:
myRange.AutoFilter Field:=1
...and I get:
I can delete the non-filtered rows with:
Rows("2:7").Delete Shift:=xlUp
Putting it all together, you could do something like:
Sub DeleteRowsExceptApplesAndBananas()
Const startCell = "A1"
Dim rgFilter As Range
'get range to filter
With Sheets("Sheet1")
Set rgFilter = Range(.Range(startCell), .Range(startCell).End(xlDown))
'set filter
rgFilter.AutoFilter 1, "<>*Banana*", xlAnd, "<>*apple*"
'delete rows beginning one below startCell's row
Range(.Range(startCell).Offset(1).Row & ":" & _
.Range(startCell).End(xlDown).Row).Delete (xlUp)
'un-filter
rgFilter.AutoFilter 1
End With
End Sub
It doesn't seem to me that Range.AutoFilter will do what you want it to here, precisely because you can only use two criteria for it.
I'd personally prefer to solve this problem with a loop operation, like so:
Option Compare Text
Sub Macro1()
Dim ws As Worksheet
Dim rng As Range
Dim col As String
Dim i As Integer
Set ws = ActiveWorkbook.Sheets("Sheet1")
col = "A"
i = 1
Set rng = ws.Range(col & i)
Do
Select Case rng.FormulaR1C1
Case "apple", "orange", "banana"
i = i + 1
Case Else
rng.Delete xlShiftUp
End Select
Set rng = ws.Range(col & i)
Loop Until rng.FormulaR1C1 = ""
End Sub
The code above assumes that you've already done all the preprocessing you've needed to do to extract your list of fruits, and that that list begins in cell A1 of Sheet1, although you can of course modify that code to put the list anywhere you'd like.
Version 1 bellow uses a "reverse" AutoFilter
Version 2, moves all rows to keep to a new sheet, and deletes the old (very fast for a lot of rows)
.
Version 1
Option Explicit
Public Sub DeleteRowsForCriteria()
Const FILTER_COL = "Q"
Const To_KEP = "apple banana orange"
Dim ws As Worksheet, lr As Long
Set ws = Sheet1 'Or: Set ws = ThisWorkbook.Worksheets("Sheet1")
lr = ws.Cells(ws.Rows.Count, FILTER_COL).End(xlUp).Row
Application.ScreenUpdating = False
ws.Range("AF1:AF" & lr).TextToColumns Destination:=ws.Range("AA1"), _
TextQualifier:=xlDoubleQuote, _
FieldInfo:=Array(1, 1), _
TrailingMinusNumbers:=True
Dim filterCol As Range, toKep As Variant, keep As Range
Set filterCol = ws.Range("Q1:Q" & lr)
toKep = Split(To_KEP)
With filterCol 'Reverse Filter - show all rows to keep ("apple banana orange")
.AutoFilter Field:=1, Criteria1:=toKep, Operator:=xlFilterValues
If .SpecialCells(xlCellTypeVisible).Count > 1 Then
Set keep = .SpecialCells(xlCellTypeVisible).EntireRow
End If
.AutoFilter 'Unhide all rows
keep.Rows.Hidden = True 'Hide all rows to keep ("apple banana orange")
.SpecialCells(xlCellTypeVisible).EntireRow.Delete 'Delete unwanted (now visible)
End With
keep.Rows.Hidden = False 'Unhide rows to keep ("apple banana orange")
Application.ScreenUpdating = True
End Sub
.
Version 2
Public Sub DeleteRowsForCriteriaFast()
Const FILTER_COL = "Q"
Const To_KEP = "apple banana orange"
Dim ws1 As Worksheet, ws2 As Worksheet, lr As Long, wsName As String, keep As Range
Set ws1 = Sheet1 'Or: Set ws = ThisWorkbook.Worksheets("Sheet1")
lr = ws1.Cells(ws1.Rows.Count, FILTER_COL).End(xlUp).Row
Application.ScreenUpdating = False
ws1.Range("AF1:AF" & lr).TextToColumns Destination:=ws1.Range("AA1"), _
TextQualifier:=xlDoubleQuote, _
FieldInfo:=Array(1, 1), _
TrailingMinusNumbers:=True
Dim filterCol As Range, toKep As Variant
Set filterCol = ws1.Range("Q1:Q" & lr)
toKep = Split(To_KEP)
Application.DisplayAlerts = False
Set ws2 = ThisWorkbook.Worksheets.Add(After:=ws1)
wsName = ws1.Name
With filterCol
.AutoFilter Field:=1, Criteria1:=toKep, Operator:=xlFilterValues
If .SpecialCells(xlCellTypeVisible).Count > 1 Then
.EntireRow.Copy
ws2.Cells.PasteSpecial xlPasteColumnWidths
ws2.Cells.PasteSpecial xlPasteAll 'Paste data on new sheet
ws1.Delete: ws2.Name = wsName: ws2.Cells(1).Select
End If
End With
Application.ScreenUpdating = True
Application.DisplayAlerts = True
Application.CutCopyMode = False
End Sub
.
TextToColumns default parameters
DataType:=xlDelimited
ConsecutiveDelimiter:=False
Tab:=False
Semicolon:=False
Comma:=False
Space:=False
Other:=False

Loop through Autofilter Criteria VBA not starting with first available result?

This script is used to filter column I data, copy it and move it to a new worksheet based on the first visible cell in I2 (header is I1). Afterwards, I would want to Loop it to go through the rest of the autofilter criteria without actually referencing anything, just running through the list. It seems to be working but it unselects all the data in Column I and doesn't name the sheet properly because the data results in blank rows. Can anyone help me?
I just need the code to do this:
Autofilter by column I (Manager), select all cells, create new worksheet, paste filtered manager data from raw data into that new worksheet, name worksheet based on first visible cell value in column I (manager name), and then loop through the rest of the filter list without having to reference manager names, just a Next kind of Looping feature until the whole list has been run-through.
Sub Format()
Set My_Range = Worksheets("Sheet1").Range("A1:I" & LastRow(Worksheets("Sheet1")))
Set Name = FirstVisibleValue(ActiveSheet, 2, 9)
Cells.Select
Do
'Filter and set the filter field and the filter criteria :
My_Range.AutoFilter Field:=9, Criteria1:=ActiveCell.Value
'Add a new Worksheet
Set WSNew = Worksheets.Add(After:=Sheets("Sheet1"))
WSNew.Name = Name
'Copy/paste the visible data to the new worksheet
My_Range.Parent.AutoFilter.Range.Copy
With WSNew.Range("A1")
.PasteSpecial xlPasteValues
Cells.Select
End With
'Close AutoFilter
My_Range.Parent.AutoFilterMode = False
'Restore ScreenUpdating, Calculation, EnableEvents, ....
My_Range.Parent.Select
ActiveWindow.View = ViewMode
If Not WSNew Is Nothing Then WSNew.Select
With Application
.ScreenUpdating = True
.EnableEvents = True
.Calculation = CalcMode
End With
Loop
End Sub
Function LastRow(sh As Worksheet)
On Error Resume Next
LastRow = sh.Cells.Find(What:="*", _
After:=sh.Range("A1"), _
Lookat:=xlPart, _
LookIn:=xlValues, _
SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious, _
MatchCase:=False).Row
On Error GoTo 0
End Function
Try this instead - cut down a lot of unnecessary things and cleaned it up a bit. To make sure we don't already have a worksheet for that manager, we use the UDF WorksheetExists().
Also I try to avoid Do/Loop loops when I can - just use a For loop for the entire column of I.
Option Explicit
Sub Format()
Dim sht As Worksheet, WSNew As Worksheet
Dim My_Range As Range
Dim i As Long, lastrow As Long
Set sht = ThisWorkbook.Worksheets("Sheet1")
lastrow = sht.Cells(sht.Rows.Count, "I").End(xlUp).Row
Set My_Range = sht.Range("A1:I" & lastrow)
For i = 2 To lastrow
If WorksheetExists(sht.Range("I" & i).Value) = False Then
Set WSNew = Worksheets.Add(After:=Sheets("Sheet1"))
WSNew.Name = sht.Range("I" & i).Value
My_Range.AutoFilter Field:=9, Criteria1:=sht.Range("I" & i).Value
My_Range.Parent.AutoFilter.Range.Copy
WSNew.Range("A1").PasteSpecial xlPasteValues
End If
Next i
My_Range.Parent.AutoFilterMode = False
Application.CutCopyMode = False
End Sub
Function WorksheetExists(sName As String) As Boolean
WorksheetExists = Evaluate("ISREF('" & sName & "'!A1)")
End Function

Sending Data to Multiple Worksheets in Excel

I'm working on a master roster that has personnel assigned to different floors. In the master column for assigned floor, I would like the information to transfer to a separate worksheet dedicated to that floor, eight floors total with there own value ( 1 equals first floor worksheet, 2 equals second floor worksheet, and so on).
Name Contact Number Assigned Floor if assigned to floor five will move all previous information to the 5th floor worksheet.
If what I'm trying to do still sounds unclear let me know, but that's the best way to describe it. Would prefer not to use VBA, but if nothing else will appreciate full code layout.
Good question.
Column A : Header in A1 = Country, A2:A? = Country names
Column B : Header in B1 = Name, B2:B? = Names
Column C : Header in C1 = Gender, C2:C? = F or M
Column D : Header in D1 = Birthday, D2:D? = Dates
1: Criteria in the code (=Netherlands, see the tips below the macro)
2: Filter on ActiveCell value
3: Filter on Range value (D1 in this example)
4: Filter on InputBox value
Sub Copy_With_AutoFilter1()
'Note: This macro use the function LastRow
Dim My_Range As Range
Dim CalcMode As Long
Dim ViewMode As Long
Dim FilterCriteria As String
Dim CCount As Long
Dim WSNew As Worksheet
Dim sheetName As String
Dim rng As Range
'Set filter range on ActiveSheet: A1 is the top left cell of your filter range
'and the header of the first column, D is the last column in the filter range.
'You can also add the sheet name to the code like this :
'Worksheets("Sheet1").Range("A1:D" & LastRow(Worksheets("Sheet1")))
'No need that the sheet is active then when you run the macro when you use this.
Set My_Range = Range("A1:D" & LastRow(ActiveSheet))
My_Range.Parent.Select
If ActiveWorkbook.ProtectStructure = True Or _
My_Range.Parent.ProtectContents = True Then
MsgBox "Sorry, not working when the workbook or worksheet is protected", _
vbOKOnly, "Copy to new worksheet"
Exit Sub
End If
'Change ScreenUpdating, Calculation, EnableEvents, ....
With Application
CalcMode = .Calculation
.Calculation = xlCalculationManual
.ScreenUpdating = False
.EnableEvents = False
End With
ViewMode = ActiveWindow.View
ActiveWindow.View = xlNormalView
ActiveSheet.DisplayPageBreaks = False
'Firstly, remove the AutoFilter
My_Range.Parent.AutoFilterMode = False
'Filter and set the filter field and the filter criteria :
'This example filter on the first column in the range (change the field if needed)
'In this case the range starts in A so Field 1 is column A, 2 = column B, ......
'Use "<>Netherlands" as criteria if you want the opposite
My_Range.AutoFilter Field:=1, Criteria1:="=Netherlands"
'If you want to filter on a cell value you can use this, use "<>" for the opposite
'This example uses the activecell value
'My_Range.AutoFilter Field:=1, Criteria1:="=" & ActiveCell.Value
'This will use the cell value from A2 as criteria
'My_Range.AutoFilter Field:=1, Criteria1:="=" & Range("A2").Value
''If you want to filter on a Inputbox value use this
'FilterCriteria = InputBox("What text do you want to filter on?", _
' "Enter the filter item.")
'My_Range.AutoFilter Field:=1, Criteria1:="=" & FilterCriteria
'Check if there are not more then 8192 areas(limit of areas that Excel can copy)
CCount = 0
On Error Resume Next
CCount = My_Range.Columns(1).SpecialCells(xlCellTypeVisible).Areas(1).Cells.Count
On Error GoTo 0
If CCount = 0 Then
MsgBox "There are more than 8192 areas:" _
& vbNewLine & "It is not possible to copy the visible data." _
& vbNewLine & "Tip: Sort your data before you use this macro.", _
vbOKOnly, "Copy to worksheet"
Else
'Add a new Worksheet
Set WSNew = Worksheets.Add(After:=Sheets(ActiveSheet.Index))
'Ask for the Worksheet name
sheetName = InputBox("What is the name of the new worksheet?", _
"Name the New Sheet")
On Error Resume Next
WSNew.Name = sheetName
If Err.Number > 0 Then
MsgBox "Change the name of sheet : " & WSNew.Name & _
" manually after the macro is ready. The sheet name" & _
" you fill in already exists or you use characters" & _
" that are not allowed in a sheet name."
Err.Clear
End If
On Error GoTo 0
'Copy/paste the visible data to the new worksheet
My_Range.Parent.AutoFilter.Range.Copy
With WSNew.Range("A1")
' Paste:=8 will copy the columnwidth in Excel 2000 and higher
' Remove this line if you use Excel 97
.PasteSpecial Paste:=8
.PasteSpecial xlPasteValues
.PasteSpecial xlPasteFormats
Application.CutCopyMode = False
.Select
End With
' If you want to delete the rows that you copy, also use this
' With My_Range.Parent.AutoFilter.Range
' On Error Resume Next
' Set rng = .Offset(1, 0).Resize(.Rows.Count - 1, .Columns.Count) _
' .SpecialCells(xlCellTypeVisible)
' On Error GoTo 0
' If Not rng Is Nothing Then rng.EntireRow.Delete
' End With
End If
'Close AutoFilter
My_Range.Parent.AutoFilterMode = False
'Restore ScreenUpdating, Calculation, EnableEvents, ....
My_Range.Parent.Select
ActiveWindow.View = ViewMode
If Not WSNew Is Nothing Then WSNew.Select
With Application
.ScreenUpdating = True
.EnableEvents = True
.Calculation = CalcMode
End With
End Sub
Function LastRow(sh As Worksheet)
On Error Resume Next
LastRow = sh.Cells.Find(What:="*", _
After:=sh.Range("A1"), _
Lookat:=xlPart, _
LookIn:=xlValues, _
SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious, _
MatchCase:=False).Row
On Error GoTo 0
End Function
From this link.
https://www.rondebruin.nl/win/s3/win006_1.htm

Migrate Master Sheet To Individual Sheets

I have a worksheet that has all data combined on it. Sample structure is like this (of course columns range all the way over to X and rows are roughly 17K)
Column A -- Column B -- Column C -- Column D -- Column E
Name1 stuff stuff stuff stuff
Name1 stuff stuff stuff stuff
Name2 stuff stuff stuff stuff
Name3 stuff stuff stuff stuff
So I am in need of 3 new worksheets added, each one named Name1, Name2, Name3 and all rows corresponding to that name be copied over to the appropriate sheet.
There is my predicament, how do I scan the worksheet for each unique name in column A, store the name in a variable so I can name a worksheet after it?
I think synax like such would be close, but not perfect -- what would proper VBA be to copy all data for each name to it's own worksheet?
ThisWorkbook.Worksheets.Add After:=ActiveSheet
ActiveSheet.Name = Blah
Set rngCopy = ActiveSheet.UsedRange
Set rngCopy = rngCopy.SpecialCells(XlCellType.xlCellTypeVisible)
ThisWorkbook.Worksheets.Add After:=ActiveSheet
ActiveSheet.Name = Blah
rngCopy.Copy ThisWorkbook.Worksheets(Blah).Cells(1, 1)
Application.CutCopyMode = False
Cells.Select
Cells.EntireColumn.AutoFit
Range("A1").Select
Sub ExtractWorksheets()
Application.ScreenUpdating = False
Dim OriginalAddress As String
Dim OriginalData
With Worksheets("Sheet1").Range("A1").CurrentRegion
OriginalData = .Value
OriginalAddress = .Address
If Not .AutoFilter Then .AutoFilter
Do While .Cells(2, 1) <> ""
.AutoFilter Field:=1, Criteria1:=.Cells(2, 1).Value
Worksheets.Add After:=ActiveSheet
ActiveSheet.Name = getCleanWorksheetName(.Cells(2, 1).Value)
.Copy Destination:=Range("A1")
.Offset(1).EntireRow.Delete
Loop
.Range(OriginalAddress).Value = OriginalData
End With
Application.ScreenUpdating = True
End Sub
'VBA Express http://www.vbaexpress.com/kb/getarticle.php?kb_id=1132
Function getCleanWorksheetName(ByRef SheetName As String) As String
Dim charPairs As Variant, ch As Variant
charPairs = Array(Array(":", "."), Array("/", "."), Array("\", ""), Array("?", "_"), Array("*", "_"), Array("[", "("), Array("]", ")"))
For Each ch In charPairs
If InStr(SheetName, ch(0)) Then SheetName = Replace(SheetName, ch(0), ch(1))
Next
getCleanWorksheetName = Left(SheetName, 31)
End Function
Try it this way.
Sub Copy_To_Worksheets()
'Note: This macro use the function LastRow
Dim My_Range As Range
Dim FieldNum As Long
Dim CalcMode As Long
Dim ViewMode As Long
Dim ws2 As Worksheet
Dim Lrow As Long
Dim cell As Range
Dim CCount As Long
Dim WSNew As Worksheet
Dim ErrNum As Long
'Set filter range on ActiveSheet: A1 is the top left cell of your filter range
'and the header of the first column, D is the last column in the filter range.
'You can also add the sheet name to the code like this :
'Worksheets("Sheet1").Range("A1:D" & LastRow(Worksheets("Sheet1")))
'No need that the sheet is active then when you run the macro when you use this.
Set My_Range = Range("A1:D" & LastRow(ActiveSheet))
My_Range.Parent.Select
If ActiveWorkbook.ProtectStructure = True Or _
My_Range.Parent.ProtectContents = True Then
MsgBox "Sorry, not working when the workbook or worksheet is protected", _
vbOKOnly, "Copy to new worksheet"
Exit Sub
End If
'This example filters on the first column in the range(change the field if needed)
'In this case the range starts in A so Field:=1 is column A, 2 = column B, ......
FieldNum = 1
'Turn off AutoFilter
My_Range.Parent.AutoFilterMode = False
'Change ScreenUpdating, Calculation, EnableEvents, ....
With Application
CalcMode = .Calculation
.Calculation = xlCalculationManual
.ScreenUpdating = False
.EnableEvents = False
End With
ViewMode = ActiveWindow.View
ActiveWindow.View = xlNormalView
ActiveSheet.DisplayPageBreaks = False
'Add a worksheet to copy the a unique list and add the CriteriaRange
Set ws2 = Worksheets.Add
With ws2
'first we copy the Unique data from the filter field to ws2
My_Range.Columns(FieldNum).AdvancedFilter _
Action:=xlFilterCopy, _
CopyToRange:=.Range("A1"), Unique:=True
'loop through the unique list in ws2 and filter/copy to a new sheet
Lrow = .Cells(Rows.Count, "A").End(xlUp).Row
For Each cell In .Range("A2:A" & Lrow)
'Filter the range
My_Range.AutoFilter Field:=FieldNum, Criteria1:="=" & _
Replace(Replace(Replace(cell.Value, "~", "~~"), "*", "~*"), "?", "~?")
'Check if there are no more then 8192 areas(limit of areas)
CCount = 0
On Error Resume Next
CCount = My_Range.Columns(1).SpecialCells(xlCellTypeVisible) _
.Areas(1).Cells.Count
On Error GoTo 0
If CCount = 0 Then
MsgBox "There are more than 8192 areas for the value : " & cell.Value _
& vbNewLine & "It is not possible to copy the visible data." _
& vbNewLine & "Tip: Sort your data before you use this macro.", _
vbOKOnly, "Split in worksheets"
Else
'Add a new worksheet
Set WSNew = Worksheets.Add(After:=Sheets(Sheets.Count))
On Error Resume Next
WSNew.Name = cell.Value
If Err.Number > 0 Then
ErrNum = ErrNum + 1
WSNew.Name = "Error_" & Format(ErrNum, "0000")
Err.Clear
End If
On Error GoTo 0
'Copy the visible data to the new worksheet
My_Range.SpecialCells(xlCellTypeVisible).Copy
With WSNew.Range("A1")
' Paste:=8 will copy the columnwidth in Excel 2000 and higher
' Remove this line if you use Excel 97
.PasteSpecial Paste:=8
.PasteSpecial xlPasteValues
.PasteSpecial xlPasteFormats
Application.CutCopyMode = False
.Select
End With
End If
'Show all data in the range
My_Range.AutoFilter Field:=FieldNum
Next cell
'Delete the ws2 sheet
On Error Resume Next
Application.DisplayAlerts = False
.Delete
Application.DisplayAlerts = True
On Error GoTo 0
End With
'Turn off AutoFilter
My_Range.Parent.AutoFilterMode = False
If ErrNum > 0 Then
MsgBox "Rename every WorkSheet name that start with ""Error_"" manually" _
& vbNewLine & "There are characters in the name that are not allowed" _
& vbNewLine & "in a sheet name or the worksheet already exist."
End If
'Restore ScreenUpdating, Calculation, EnableEvents, ....
My_Range.Parent.Select
ActiveWindow.View = ViewMode
With Application
.ScreenUpdating = True
.EnableEvents = True
.Calculation = CalcMode
End With
End Sub
Function LastRow(sh As Worksheet)
On Error Resume Next
LastRow = sh.Cells.Find(What:="*", _
After:=sh.Range("A1"), _
Lookat:=xlPart, _
LookIn:=xlValues, _
SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious, _
MatchCase:=False).Row
On Error GoTo 0
End Function
You'll find more info here.
http://www.rondebruin.nl/win/s3/win006_4.htm

Sort Column Based on Another Column Order of Values

I have two columns in spreadsheet1:
Col1 Col2
1 PDC
2 SR3
3 PDC
4 VBM
5 VBM
6 GAL
7 VBM
8 GAL
9 PDC
I have 1 column in spreadsheet2:
Col1
PDC
SR3
VBM
GAL
How can I sort Col1 and Col2 from spreadsheet1 based on the order in spreadsheet2 Col1?
Try this code:
Sub test()
Dim wb As Workbook
Dim ws1 As Worksheet
Dim ws2 As Worksheet
Dim lastrow As Long
Dim rng As Range
'if workbook2 is already opened
Set wb = Workbooks("Book2") ' change Book2 to suit
'if workbook2 is not opened
'Set wb = Workbooks.Open("C:\Book2.xlsx")
'change sheet1 to suit
Set ws1 = ThisWorkbook.Worksheets("Sheet1")
'change sheet2 to suit
Set ws2 = wb.Worksheets("Sheet2")
With ws1
'change column B to column with your values "PDC", "SR3" and so on
lastrow = .Cells(.Rows.Count, "B").End(xlUp).Row
'change to address of range you wnat to sort
Set rng = .Range("A1:B" & lastrow)
With rng.Offset(, rng.Columns.Count).Resize(, 1)
.EntireColumn.Insert
.Offset(, -1).FormulaR1C1 = "=MATCH(RC[-1],'[" & wb.Name & "]" & ws2.Name & "'!C1:C1,0)"
.Offset(, -1).Value = .Offset(, -1).Value
End With
With rng.Resize(, rng.Columns.Count + 1)
.Sort Key1:=.Cells(1, .Columns.Count), Order1:=xlAscending, Header:=xlGuess
.Cells(1, .Columns.Count).EntireColumn.Delete
End With
End With
wb.Close
End Sub
Explanaiton:
There're two workbooks. Code should be places in first workbook (where you want to sort range)
The main idea is to add temporary column with formula like =MATCH(B1,[Book2]Sheet2!A:A,0) to get row numbers of values from column B (workbook1) in column A (workbook2).
Sort range based on this numbers.
Delete temporary column
Notes:
Next line assumed that column with "PDC", "SR3" (in workbook1) is last in selected range (Set rng = .Range("A1:B" & lastrow)):
.Offset(, -1).FormulaR1C1 = "=MATCH(RC[-1],'[" & wb.Name & "]" & ws2.Name & "'!C1:C1,0)"
if it's not true, change RC[-1] to, say RC[-2] if this column is last but one and so on.
C1:C1 part of formula means that in workbook2 column with "PDC", "SR3" is column A (column №1). If it's not true, change it to, say, C5:C5, which means that column is E (column №5).
I found this code which seems a bit simpler and works great.
Sub NewSortTest()
Dim keyRange As Variant
Dim sortNum As Long
keyRange = ActiveWorkbook.Worksheets("Sheet2").Cells.Range("A1:A10").Value
Application.AddCustomList ListArray:=keyRange
sortNum = Application.CustomListCount
ActiveWorkbook.Worksheets("Sheet1").Sort.SortFields.Clear
ActiveWorkbook.Worksheets("Sheet1").Sort.SortFields.Add key:=Range("A1:A20"), SortOn:=xlSortOnValues, Order:=xlAscending, CustomOrder:=sortNum, DataOption:=xlSortNormal
With ActiveWorkbook.Worksheets("Sheet1").Sort
.SetRange Range("A1:B20")
.Header = xlGuess
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
End Sub