2011-08-23 343 views
4

我知道我可以通過遍歷第一個範圍來完成此任務,但我很好奇我是否可以使用SpecialCells屬性來完成此任務。如何將一個範圍的SpecialCells分配到新範圍?

說我有一個空單元格名稱之間的列:

A  B C 
Jon 
Jim 
Sally 

Jane 


Mary 

如果我想使用VBA在剛剛用過的細胞複製,我可以說

Range("A1:A8").SpecialCells(xlCellTypeConstants, xlTextValues).Copy 
Range("C1:C"&Range("A1:A8").SpecialCells(xlCellTypeConstants, xlTextValues).Count).PasteSpecial 

和結束與

A  B C 
Jon  Jon 
Jim  Jim 
Sally  Sally 
      Jane  
Jane  Mary 


Mary 

相反,我想能夠做到這一點,而無需在任何地方粘貼範圍。

我想要做的是有一個範圍包含[Jon,Jim,Sally,Jane,Mary],但如果我嘗試Set rng = Range("A:A").SpecialCells(xlCellTypeConstants,xlTextValues),我最終將空格作爲範圍的元素,或使用硬編碼的單元格範圍,一個在擊中空間之前僅計入[Jon, Jim, Sally]

我希望能夠在代碼中的其他地方使用範圍,我認爲SpecialCells是一個很好的緊湊方式,但是我唯一的選擇是在循環中做它,並將單元格作爲<> ""

回答

4

考慮下面的代碼

Dim r As Range 
Set r = Range("A:A").SpecialCells(xlCellTypeConstants, xlTextValues) 

Debug.Print r.Rows.Count, r.Cells.Count 
' returns:  3   5 

在上述唯一可靠的一條信息是r.Cells.CountRows在第一張空白處被剪下。我想這會混淆整個粘貼過程。所以,你不能直接粘貼r到工作表。

你可以將它轉移到一個Variant數組,然後將它打到工作表上。但如何做到這一點?那麼,r.Cells類似於一個集合。也許它轉換成一個這樣的數組:

Dim i As Long 
Dim c As Range 
Dim v As Variant 
ReDim v(1 To r.Cells.Count, 1 To 1) 
i = 0 
For Each c In r 
    i = i + 1 
    v(i, 1) = c 
Next c 
Range("B1").Resize(UBound(v,1),UBound(v,2)) = v 

不需要檢查空單元。

您也可以使用Chip Pearson的CollectionToArray procedure,這基本上是上述代碼的更有趣的實現,可能稍作修改。

順便說一下,檢查<> ""將不會拒絕其值爲空字符串""的單元格。如果您必須檢查真空/「空白」單元,則IsEmpty更安全。

*在@Inssun上發佈的信息用於在此上下文中創造「巴掌」。

+0

你確定最後一部分?我已經測試過了,如果你在檢查len(cell)<> 0之前做了Range(「A5」)。Value =「」,它會拒絕這個單元格。 – aevanko

+0

只是澄清,我說的是len(文本)<> 0。我同意檢查<>「是否是一個可怕的想法。 – aevanko

+0

v爲什麼要打擾第二維,只是好奇而已? – jonsca

1

由於Range對象代表實際的細胞(A1A2A3,在這種情況下A5A8),我不認爲你可以在5個連續細胞緊湊型,而不會有任何粘貼。

但是,如果你需要在其上循環,你不需要使用的比較,使用For Each將跳過空白:

Set Rng = Range("A1:A8").SpecialCells(xlCellTypeConstants, xlTextValues) 
Print Rng.count 
5 
For Each cell in Rng: Print cell: Next cell 
Jon  
Jim  
Sally  
Jane  
Mary  

它可能不是很多,但它可以幫助你根據你想達到什麼目的。

+0

您是否認爲複製/粘貼在內部執行了類似的操作?我也在用'SelectedRange'嘗試一些東西,但我不認爲這也是可行的。 – jonsca

+0

我想是的。無論如何,'Range'確實表示這五個單元,如For Each循環和Count屬性所證明。然而細胞指數是1,2,3,5和8.('Print Rng(8)'給瑪麗)。其他指標並不完全在'Range'範圍內,但如果您嘗試引用它們,您會得到空白。由於「範圍」與表單中的特定單元格相關聯(並且我認爲您不能這樣做),因此無法將其壓縮以除去空洞,而不會刪除A4,A6和A7。恐怕我沒有看到任何其他可能有用的東西。 – Joubarc

2

如果你真的想要使用specialcells,你需要爲每個循環做一次,但是這裏是如何做到這一點,沒有變量數組或檢查空單元格。請注意,我使用反向字典(請參閱下面的註釋)將單元格存儲爲項目(而不是鍵),以便我可以利用.Items方法將字典中所有項目的數組吐出。

Sub test() 

Dim cell As Range 
Dim i As Long 
Dim dict As Object 
Set dict = CreateObject("scripting.dictionary") 

For Each cell In Range("A1:A10").SpecialCells(xlCellTypeConstants) 
    dict.Add i, cell.Value 
    i = i + 1 
Next 

Sheet1.Range("c1").Resize(dict.Count).Value = _ 
Application.Transpose(dict.items) 

End Sub 

但是有一種更快的方法可以做到這一點,以防您正在使用相當大的範圍。使用字典對象是因爲它能夠吐出其中所有鍵/項目的數組(可以將其轉置爲一個範圍)。集合不具備這種可靠性,但字典只允許每個關鍵字中的一個。解決方法?反過來使用字典,將您的值作爲項目和鑰匙計數器!

這裏的(允許有受騙者)如何使用變量數組/詞典的例子:

Sub test() 

Dim vArray As Variant 
Dim i As Long, j As Long, k As Long 
Dim dict As Object 
Set dict = CreateObject("scripting.dictionary") 

vArray = Range("A1:A10").Value 

For i = 1 To UBound(vArray, 1) 
    For j = 1 To UBound(vArray, 2) 
     If Len(vArray(i, j)) <> 0 Then 
      dict.Add k, vArray(i, j) 
      k = k + 1 
     End If 
    Next 
Next 

Sheet1.Range("c1").Resize(dict.Count).Value = _ 
Application.Transpose(dict.items) 

End Sub 

這是比使用特殊的細胞(因爲VBA處理工作,而諮詢的Excel)快得多和你可以像使用粘貼一樣使用轉置,所以我更喜歡這種方法。

正如JFC指出的那樣,使用字典對象似乎並沒有太多好處,最大的原因是它很容易轉換數組,並且可以水平轉置它,這非常有趣。

Sheet1.Range(Cells(1, 3), Cells(1, dict.Count + 2)).Value = _ 
Application.Transpose(Application.Transpose(dict.items)) 
+0

我喜歡它! SpecialCells是否只是在內部迭代,你碰巧知道嗎?我一直認爲它使用了一些已經存在的元數據。 – jonsca

+0

基本上,「我唯一的選擇是做一個循環,並將單元格比作<>」「」 –

+0

我不知道實際的實現,但我最近對它的運行時間進行了詳盡的測試,發現變體+ len檢查仍然快得多。 – aevanko