2014-07-03 94 views
0

我希望能够在过滤的表格中找到连续行的最大可见区域。我知道一种可能的方式是使用“xlCellTypeVisible”属性循环可见单元格并计算每个可见区域中的单元格。然而,这些数据由数十个甚至数十万个行组成,所以我想知道是否有更快,更有效的方法来做到这一点。在已过滤的范围内查找可见单元格的最大范围

回答

1

几个月前,我有一个模糊的相似要求,对我发现的最佳解决方案感到不满。盯着你的问题,我突然想到了两种新技术。下面的宏展示了两者。虽然我无法想象技术2不是更快的情况,但两者都给出了可接受的结果。

我的宏启动:

Option Explicit 
Sub LargestVisibleRange() 

    Dim Count As Long 
    Dim NumRowsInLargestRange As Long 
    Dim RngCrnt As Range 
    Dim RngTgt As Range 
    Dim RowCrnt As Long 
    Dim RowCrntRangeStart As Long 
    Dim RowLargestRangeEnd As Long 
    Dim RowLargestRangeStart As Long 
    Dim RowMax As Long 
    Dim RowPrev As Long 
    Dim StartTime As Single 

    With Worksheets("TrainData") 

    RowMax = .Cells(Rows.Count, "A").End(xlUp).Row 
    Debug.Print "1 RowMax " & RowMax 

    .Cells.AutoFilter 
    .Range(.Cells(2, 1), .Cells(RowMax, "Z")).AutoFilter Field:=2, Criteria1:=ChrW$(&H2116) & " 9/10" 

我有一些数据时,我与过滤实验我使用。如果你想使用这个宏作为你自己的实验的基础,你将不得不取代上面的陈述。

宏继续:

Set RngTgt = .Range(.Rows(2), .Rows(RowMax)).SpecialCells(xlCellTypeVisible) 
Debug.Print "2 RngTgt " & RngTgt.Address 
Count = 1 
Debug.Print "3 "; 
For Each RngCrnt In RngTgt 
    Debug.Print RngCrnt.Address & " "; 
    Count = Count + 1 
    If Count = 30 Then Exit For 
Next 
Debug.Print 

Set RngTgt = RngTgt.EntireRow 
Debug.Print "4 RngTgt " & RngTgt.Address 

Count = 1 
Debug.Print "5 "; 
For Each RngCrnt In RngTgt 
    Debug.Print RngCrnt.Address & " "; 
    Count = Count + 1 
    If Count = 30 Then Exit For 
Next 
Debug.Print 

从上面的语句中的输出是:

1 RowMax 5691 
2 RngTgt $2:$4,$20:$22,$38:$40,$56:$58,$74:$76,$92:$94,$110:$112,$128:$130,$146:$148,$164:$166,$182:$184,$200:$202,$218:$220,$236:$238,$254:$256,$272:$274,$290:$292,$308:$310,$326:$328,$344:$346,$362:$364,$380:$382,$398:$400,$416:$418,$434:$436,$452:$454,$470:$472 
3 $A$2 $B$2 $C$2 $D$2 $E$2 $F$2 $G$2 $H$2 $I$2 $J$2 $K$2 $L$2 $M$2 $N$2 $O$2 $P$2 $Q$2 $R$2 $S$2 $T$2 $U$2 $V$2 $W$2 $X$2 $Y$2 $Z$2 $AA$2 $AB$2 $AC$2 
4 RngTgt $2:$4,$20:$22,$38:$40,$56:$58,$74:$76,$92:$94,$110:$112,$128:$130,$146:$148,$164:$166,$182:$184,$200:$202,$218:$220,$236:$238,$254:$256,$272:$274,$290:$292,$308:$310,$326:$328,$344:$346,$362:$364,$380:$382,$398:$400,$416:$418,$434:$436,$452:$454,$470:$472 
5 $2:$2 $3:$3 $4:$4 $20:$20 $21:$21 $22:$22 $38:$38 $39:$39 $40:$40 $56:$56 $57:$57 $58:$58 $74:$74 $75:$75 $76:$76 $92:$92 $93:$93 $94:$94 $110:$110 $111:$111 $112:$112 $128:$128 $129:$129 $130:$130 $146:$146 $147:$147 $148:$148 $164:$164 $165:$165 

线1示出了,我有5690个的数据行。这比你少得多,但这足以提供足够的性能指标。

线2的结果是:

Set RngTgt = .Range(.Rows(2), .Rows(RowMax)).SpecialCells(xlCellTypeVisible) 
Debug.Print "2 RngTgt " & RngTgt.Address 

注意,地址范围为$ 2:$ 4,$ 20:$ 22和等。还请注意该行被截断。 Address属性给出尽可能多的整个范围,使得字符串的总长度小于255个字符。

3行的结果是:

Debug.Print "3 "; 
For Each RngCrnt In RngTgt 
    Debug.Print RngCrnt.Address & " "; 
    Count = Count + 1 
    If Count = 30 Then Exit For 
Next 
Debug.Print 

注意,虽然范围内的地址分别为整个行,For Each返回单个细胞。还要注意,尽管我有26列数据,但返回的单元格包括AA2,AB2等。

4号线的结果是:这样看来

Set RngTgt = RngTgt.EntireRow 
Debug.Print "4 RngTgt " & RngTgt.Address 

,新Set RngTgt一直没有效果。

但是,第5行与第3行相同,包含行而不是单元格。如果使用Excel 2003,处理修改后的RngTgt将比处理未修改的RngTgt快256倍。如果您使用更高版本的Excel,则速度会更快16,384。

宏的其余部分通过两种不同技术中的每一种识别最大范围。第一种技术检查每行的隐藏属性。第二种技术使用修改后的RngTgt。输出是:

Duration 1: 0.073 
Largest range 579 to 582 
Duration 2: 0.003 
Largest range 579 to 582 

我相信持续时间1表明技术1会给出可接受的结果,但技术2显然明显快得多。

的宏的其余部分是:

StartTime = Timer 

    RowCrntRangeStart = 0   ' No current visible range 
    RowLargestRangeStart = 0  ' No range found so far 

    RowCrnt = 2 
    Do While True 
     ' Search for visible row 
     Do While True 
     If Not .Rows(RowCrnt).Hidden Then 
      RowCrntRangeStart = RowCrnt 
      Exit Do 
     End If 
     RowCrnt = RowCrnt + 1 
     If RowCrnt > RowMax Then 
      Exit Do 
     End If 
     Loop 

     If RowCrntRangeStart = 0 Then 
     ' No unprocessed visible row found 
     Exit Do 
     End If 

     ' Search for invisble row 
     Do While True 
     If .Rows(RowCrnt).Hidden Then 
      ' Visible range is RowCrntRangeStart to RowCrnt-1 
      If RowLargestRangeStart = 0 Then 
      ' This is the first visible range 
      RowLargestRangeStart = RowCrntRangeStart 
      RowLargestRangeEnd = RowCrnt - 1 
      NumRowsInLargestRange = RowLargestRangeEnd - RowLargestRangeStart + 1 
      Else 
      ' Check for new range being larger thsn previous 
      If RowCrnt - RowCrntRangeStart > NumRowsInLargestRange Then 
       ' This visible range is larger than previous largest 
       RowLargestRangeStart = RowCrntRangeStart 
       RowLargestRangeEnd = RowCrnt - 1 
       NumRowsInLargestRange = RowLargestRangeEnd - RowLargestRangeStart + 1 
      End If 
      End If 
      RowCrntRangeStart = 0  ' Not within visible range 
      RowCrnt = RowCrnt + 1  ' Step over first row of invisible range 
      Exit Do 
     End If 
     RowCrnt = RowCrnt + 1 
     If RowCrnt > RowMax Then 
      Exit Do 
     End If 
     Loop 

     If RowCrnt > RowMax Then 
     Exit Do 
     End If 

    Loop 

    Debug.Print "Duration 1: " & Format(Timer - StartTime, "##0.####") 
    Debug.Print "Largest range " & RowLargestRangeStart & " to " & RowLargestRangeEnd 

    End With 

    StartTime = Timer 

    RowCrntRangeStart = 0   ' No current visible range 
    RowLargestRangeStart = 0  ' No range found so far 

    For Each RngCrnt In RngTgt 
    If RowCrntRangeStart = 0 Then 
     ' Start of visible range 
     RowPrev = RngCrnt.Row 
     RowCrntRangeStart = RowPrev 
    Else 
     ' Already within visible range 
     If RowPrev + 1 = RngCrnt.Row Then 
     ' Within same visible range 
     RowPrev = RngCrnt.Row 
     Else 
     ' Have start of new visible range 
     ' Last visible range was RowCrntRangeStart to Rowprev 
     If RowLargestRangeStart = 0 Then 
      ' This is the first visible range 
      RowLargestRangeStart = RowCrntRangeStart 
      RowLargestRangeEnd = RowPrev 
      NumRowsInLargestRange = RowLargestRangeEnd - RowLargestRangeStart + 1 
     Else 
      ' Check for new range being larger thsn previous 
      If RowPrev - RowCrntRangeStart + 1 > NumRowsInLargestRange Then 
      ' This visible range is larger than previous largest 
      RowLargestRangeStart = RowCrntRangeStart 
      RowLargestRangeEnd = RowPrev 
      NumRowsInLargestRange = RowLargestRangeEnd - RowLargestRangeStart + 1 
      End If 
     End If 
     RowCrntRangeStart = RngCrnt.Row  ' Start of new visible range 
     RowPrev = RngCrnt.Row 
     End If 
    End If 
    Next 

    Debug.Print "Duration 2: " & Format(Timer - StartTime, "##0.####") 
    Debug.Print "Largest range " & RowLargestRangeStart & " to " & RowLargestRangeEnd 

End Sub 

我希望技术2是对你有帮助。这一定对我有帮助。

+0

感谢您分享解决方案。我发现了另一种使用split为结果数组完成此操作的方法 – EranG