2014-07-03 136 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