我知道我可以通过迭代第一个范围来完成此任务,但我很想知道是否可以使用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是一种很好的紧凑方式,但我是唯一的替代方法,它可以在循环中进行,并将单元格比作{{ 1}}?
答案 0 :(得分: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.Count
。 Rows
在第一个空格处被切断。我想这会混淆整个粘贴过程。因此,您无法将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
会更安全。
*在此上下文中为@Issun提供“slap”的信用。
答案 1 :(得分:2)
如果你真的想要使用特殊的单元,你需要为每个循环做一个,但是这里是如何在没有变量数组或检查空单元格的情况下进行的。请注意,我正在反向使用字典(请参阅下面的注释)将单元格存储为项目(而非键),因此我可以使用.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
但是如果您使用相当大的范围,有一种更快的方法可以做到这一点。使用字典对象,因为它能够吐出其中的所有键/项的数组(您可以将其转置到范围)。集合没有这种可靠性,但字典只允许每个密钥中的一个。工作?反向使用字典,将值作为项目和键的计数器放置!
以下是如何使用变体数组/字典(允许使用dupes)的示例:
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所指出的那样,使用字典对象似乎并没有太多好处,最大的原因是它可以很容易地转换数组,你也可以将它水平转置,这非常有趣。 / p>
Sheet1.Range(Cells(1, 3), Cells(1, dict.Count + 2)).Value = _
Application.Transpose(Application.Transpose(dict.items))
答案 2 :(得分:1)
由于Range对象代表实际的单元格(A1
,A2
,A3
,A5
和A8
(在本例中),我不认为你可以压缩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
它可能不多,但它可能会帮助你取决于你想要达到的目标。