我怎样才能将一个范围的SpecialCells分配到一个新的范围?

我知道我可以通过遍历第一个范围来完成这个任务,但是我很好奇,看看我是否可以使用SpecialCells属性来完成它。

假设我有一列中间有空格的名字:

  ABC Jon Jim Sally Jane Mary 

如果我想用VBA复制刚才使用过的单元格,我可以这样说

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

并最终结束

  ABC 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是一个很好的紧凑的方法,但是我唯一的select是在循环中进行比较,并将比较单元格作为<> ""

考虑下面的代码

 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第一个空白处被剪切。 我想这会混淆整个粘贴过程。 所以,你不能直接粘贴到工作表。

你可以将它转移到一个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过程 ,这个过程基本上是上面代码的一个更好的实现,也许稍微修改一下。

顺便说一句,检查<> ""不会拒绝其值为空string"" 。 如果您必须检查真空/“空白”单元,那么IsEmpty更安全。

*在这个上下文中,@Issun的信用来创造“巴掌”。

如果你真的想使用specialcells,你需要为每个循环做一个,但是这里是如何做到这一点,没有一个变种arrays或检查空单元格。 请注意,我正在使用逆向字典(请参阅下面的注释)将单元格存储为项目(而不是键),所以我可以利用.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 

但有一个更快的方法来做到这一点,如果你正在一个相当大的范围工作。 使用字典对象,因为它能够吐出一个其中所有的键/项目(你可以转置到一个范围)的数组。 集合不具有这种可靠性,但是字典只允许每个关键字之一。 周围的工作? 反过来使用字典,把你的值作为项目和钥匙计数器!

下面是一个如何使用variant数组/字典的例子(允许模糊):

 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)) 

由于Range对象表示实际的单元格(在这种情况下为A1A2A3A5A8 ),所以我不认为可以在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 

这可能不是很多,但它可能会帮助你取决于你想达到什么。