
[英]Using built in sort function multiple times causes Runtime-error: '1004' The sort reference is not valid
[英]There is a way to sort columns without using the built in sort function?
我非常希望VBA执行以下操作
1)剪切选定的行
2)按字母顺序将行插入正确的位置(基于col C)
我不能使用sort的原因是因为我对一个工作表有大量引用,而当我使用sort时,即使它们中都包含$,它也会弄乱所有引用。 我发现切割可以解决问题
我相信下面的宏可以满足您的要求。
常量ColSort
定义了我已设置为C的排序列。常量RowDataFirst
定义了第一数据行。 我的测试数据有两个标题行。 根据需要更改RowDataFirst
的值。
我只对一个测试工作表进行了排序,但我相信该宏将可用于任意数量的行和列。
我从工作表“ SortSrc”到“ SortDest”进行排序。 这些工作表的名称由常量WkShtNameDest
和WkShtNameSrc
定义。 根据需要更改这些常数。
我包括了外壳排序的VBA实现。 这不是最好的排序方式,但是我掌握了例行程序,因此您将无法对足够的数据进行排序。
它创建一个包含列C的值和行号的数组。 我对该索引数组进行排序。 我使用排序的索引数组来控制数据从源工作表到目标的复制。
我希望我已经发表了足够的评论。 如有必要,请提问。
Option Explicit
Sub SortByCutNPaste()
Const ColSort As String = "C"
Const RowDataFirst As Long = 3
Const WkShtNameDest As String = "SortDest"
Const WkShtNameSrc As String = "SortSrc"
Dim ColMax As Long
Dim InxSort As Long
Dim SortArray() As String
Dim RangeDest As Range
Dim RangeSrc As Range
Dim RowDestCrnt As Long
Dim RowMax As Long
Dim RowSrcCrnt As Long
With Sheets(WkShtNameSrc)
' Find the maximum used row and maximum used column
RowMax = .Cells.Find("*", .Range("A1"), xlFormulas, , xlByRows, xlPrevious).Row
ColMax = .Cells.Find("*", .Range("A1"), xlFormulas, , xlByColumns, xlPrevious).Column
End With
' Size sort array so one entry per data row
ReDim SortArray(1 To RowMax - RowDataFirst + 1)
' Build sort array with each entry containing:
' Value of column C Nul Row number padded to three digits
' The Nul is used as a low value in case any cell value ends in what looks
' like a row number. For example:
' Row 1 Value ABC001
' Row 2 Value ABC
' would give sort keys ABC001001 and ABC002 which would be sorted incorrectly.
' Keys ABC001(0)001 and ABC(0)002 will sort incorrectly.
' Use LCase(.Cells(RowSrcCrnt, ColSort).Value) if you want a case insensitive sort.
' I have padded row numbers to three digits since you say you have 100 rows.
InxSort = LBound(SortArray)
With Sheets(WkShtNameSrc)
For RowSrcCrnt = RowDataFirst To RowMax
SortArray(InxSort) = .Cells(RowSrcCrnt, ColSort).Value & _
Chr(0) & Right("000" & RowSrcCrnt, 3)
InxSort = InxSort + 1
Next
End With
' Sort array
Call ShellSort(SortArray, UBound(SortArray))
' Prepare destination worksheet
With Sheets(WkShtNameDest)
' Clear any existing contents
.Cells.EntireRow.Delete
End With
' Copy column widths
With Sheets(WkShtNameSrc)
.Rows(1).EntireRow.Copy
End With
With Sheets(WkShtNameDest)
.Rows(1).PasteSpecial Paste:=xlPasteColumnWidths, Operation:=xlNone, _
SkipBlanks:=False, Transpose:=False
End With
' For InxSort = LBound(SortArray) To UBound(SortArray)
' Debug.Print SortArray(InxSort)
' Next
' Copy heading rows from source to destination
' Note source and destination row numbers are the same
' so use RowSrcCrnt for both worksheets.
For RowSrcCrnt = 1 To RowDataFirst - 1
With Sheets(WkShtNameSrc)
Set RangeSrc = .Range(.Cells(RowSrcCrnt, 1), .Cells(RowSrcCrnt, ColMax))
End With
With Sheets(WkShtNameDest)
Set RangeDest = .Range(.Cells(RowSrcCrnt, 1), .Cells(RowSrcCrnt, ColMax))
End With
RangeSrc.Copy Destination:=RangeDest
Next
' Copy data rows in index sequence
RowDestCrnt = RowDataFirst
For InxSort = LBound(SortArray) To UBound(SortArray)
RowSrcCrnt = Val(Right(SortArray(InxSort), 3))
With Sheets(WkShtNameSrc)
Set RangeSrc = .Range(.Cells(RowSrcCrnt, 1), .Cells(RowSrcCrnt, ColMax))
End With
With Sheets(WkShtNameDest)
Set RangeDest = .Range(.Cells(RowDestCrnt, 1), .Cells(RowDestCrnt, ColMax))
End With
RangeSrc.Copy Destination:=RangeDest
RowDestCrnt = RowDestCrnt + 1
Next
End Sub
Public Sub ShellSort(ByRef arrstgTgt() As String, ByVal inxLastToSort As Integer)
' Converted by Tony Dallimore in 2005 from Pascal routine in "Algorithms"
' by Robert Sedgewick (2nd edition) published 1989 by Addison-Wesley.
' The most basic sort is the insertion sort in which adjacent elements are compared
' and swapped as necessary. This can be very slow if the smallest elements are at
' end. ShellSort is a simple extension which gains speed by allowing exchange of
' elements that are far apart.
' The idea is to rearrange the file to give it the property that taking every h-th
' element (starting anywhere) yields a sorted file. Such a file is said to be
' h-sorted. Put another way, an h-sorted file is h independent sorted files,
' interleaved together. By h-sorting for large value of H, we can move elements
' in the array long distances and thus make it easier to h-sort for smaller values of
' h. Using such a procedure for any sequence of values of h which ends in 1 will
' produce a sorted file.
' This program uses the increment sequence: ..., 1093, 364, 121, 40, 13, 4, 1. This
' is known to be a good sequence but cannot be proved to be the best.
' The code looks faulty but it is not. The inner loop compares an
' entry with the previous in the sequence and if necessary moves it back down the
' sequence to its correct position. It does not continue with the rest of the sequence
' giving the impression it only partially sorts a sequence. However, the code is not
' sorting one sequence then the next and so on. It examines the entries in element
' number order. Having compared an entry against the previous in its sequence, it will
' be intH loops before the next entry in the sequence in compared against it.
' arrstgTgt The array to be sorted.
' inxLastToSort Elements lbound(arrstgTgt) to inxLastToSort are to be sorted.
Dim intNumRowsToSort As Integer
Dim intLBoundAdjust As Integer
Dim intH As Integer
Dim inxRowA As Integer
Dim inxRowB As Integer
Dim inxRowC As Integer
Dim stgTemp As String
'Dim intComps As Integer
'Dim intSwaps As Integer
intNumRowsToSort = inxLastToSort - LBound(arrstgTgt) + 1
intLBoundAdjust = LBound(arrstgTgt) - 1
' Set intH to 1, 4, 13, 40, 121, ..., 3n+1, ... until intH > intNumRowsToSort
intH = 1
Do While intH <= intNumRowsToSort
intH = 3 * intH + 1
Loop
Do While True
If intH = 1 Then Exit Do
' The minimum value on entry to this do-loop will be 4 so there is at least
' one repeat of the loop.
intH = intH \ 3
For inxRowA = intH + 1 To intNumRowsToSort
stgTemp = arrstgTgt(inxRowA + intLBoundAdjust)
inxRowB = inxRowA
Do While True
' The value of element inxRowA has been saved. Now move the element intH back
' from row inxRowA into this row if it is smaller than the saved value. Repeat
' this for earlier elements until one is found that is larger than the saved
' value which is placed in the gap.
inxRowC = inxRowB - intH
If arrstgTgt(inxRowC + intLBoundAdjust) <= stgTemp Then Exit Do
arrstgTgt(inxRowB + intLBoundAdjust) = arrstgTgt(inxRowC + intLBoundAdjust)
inxRowB = inxRowC
If inxRowB <= intH Then Exit Do
Loop
arrstgTgt(inxRowB + intLBoundAdjust) = stgTemp
Next
Loop
End Sub
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.