假设我定义了一个很大的不连续范围,可能是Range("B:B,E:E,F:F")
。我如何进入索引范围以将其视为连续的。
E.g。我想做一些像
这样的事情Set myRange = Range("B:B,E:E,F:F")
v = myRange.ContiguousIndex(5, 3).Value 'retrieves the value in cell F5 (row 5 col 3)
我所知道的每个方法都会根据范围中的第一个单元格(“B1”)进行偏移,并且很乐意超出该范围的范围,溢出到工作簿的其余内容中。这意味着尝试访问第5行,第3列将获得D5,就好像列C和D在我试图索引的范围内。
我尝试过Range.Cells,Range.Offset和Range.Range,但似乎都表现出同样的溢出效应。
我想到的另一种方法是将值分配给变量数组并从那里手动索引,但这很快变得复杂,因为像
这样的简单代码片段Dim v() As Variant
v = myRange
只会将不连续范围的第一个区域分配到数组中,留下一个(20 ^ 20-1)x1数组并完全忽略myRange的其余部分。因此,如果我循环遍历所有区域并将它们分别分配到我继续重新分配的数组中,那么将整个myRange放入数组中可能是可行的,但这远非易事而是我最终得到的内存比我想要的内存多得多(除非我把更多的开销用于修剪它或者我随意选择较少数量的行来复制)。
此时,手动遍历区域并自行编制索引会更加高效和简单,而无需将所有内容放入数组中。最后的方法是我目前正在做的。
问题
是否有任何现有的方法或技巧可以用来对待myRange,好像它以我描述的方式连续,并以忽略不连续性的方式索引到myRange?
TL; DR如果我有
Set myRange = Range("B:B,E:E,F:F")
v = myRange.ContiguousIndex(5, 3).Value
我想要一些方法ContiguousIndex来返回Range(“F5”)。值而不必完成手动检查Range.Areas和处理所有索引的所有工作。
奖金问题
假设myRange为Range("E:E,B:B,F:F")
(注意不同的列顺序)。有没有一种很好的方法可以将E视为第一列,将B视为第二列,将F视为第三列,这样
Set myRange = Range("E:E,B:B,F:F")
v = myRange.ContiguousIndex(5, 2).Value 'retrieves the value in cell B5
返回B5的值?这是我正在使用的方法的属性,我很乐意继续使用。
同样,我的功能也有效,但我猜测在Excel的所有怪癖中都隐藏着某种奇妙的方法或技巧,甚至更好。
答案 0 :(得分:2)
需要注意的是,使用.Cells
/ .Rows
/ .Columns
/ ._Default
,您可以获得超出范围的值:
Set myRange = Range("E2:E4,C4:B2,F2:F4") ' C4:B2 gets B2:C4
Debug.Print myRange.Areas(2)(1).Address ' $B$2
Debug.Print myRange.Areas(2)(0, 0).Address ' $A$1
Debug.Print myRange.Areas(2).Cells(0, 0).Address ' $A$1
Debug.Print myRange.Areas(2).Rows(0).Columns(0).Address ' $A$1
相反,如果您将值编入索引:
Debug.Print myRange.Areas(2).Value2(1, 1) ' value of B2
Debug.Print myRange.Areas(2).Value2(0, 0) ' Run-time error '9': Subscript out of range
如果你有任何机会有多个列,例如"E:E,A:B"
,那么如果你将每列指定为一个单独的区域,那么对它们进行索引会更容易:"E:E,A:A,B:B"
答案 1 :(得分:2)
我会发布自己的解决方案,以防其他人遇到类似的问题。这是唯一对我有用的,因为其他答案和评论依赖于了解范围内的区域(例如,依赖于每个区域是整个单列,我无法保证,因为我的范围是用户输入,可以跨越多列或有限行数。
' Indexes into a discontiguous area as expected, ignoring cells not in Range r
' and treating areas as concatenated (and top-aligned) in the order they are specified
Public Function ContiguousIndex(r As Range, row As Long, col As Long)
Dim area As Range
For Each area In r.Areas
If col <= area.Columns.count Then
If row <= area.Rows.count Then
ContiguousIndex = area.Cells(row, col)
Exit Function
Else
Err.Raise vbObjectError + 9, , "Row Index out of bounds"
End If
Else
col = col - area.Columns.count
End If
Next
' col argument > sum of all cols in all areas
Err.Raise vbObjectError + 9, , "Col Index out of bounds"
End Function
值得重新注意我在评论中介绍的内容,但可能会出乎意料:此代码将对齐所有区域,使区域1中的第一行与区域2中的第一行处于同一索引是相同的...等等。这会在调用像ContiguousIndex(Range("A1:B7,A8:B10"), 9, 2)
这样的东西时产生怪癖。虽然很明显这应该返回B9
,但事实并非如此 - 它实际上会尝试访问A1:B7
的第9行第2列,从而导致错误。这是因为两个不连续的范围,尽管它们在实际工作表上从上到下清楚地排列,但它们被视为是左右对齐。因此可以通过命令B9
(非直观地)访问ContiguousIndex(Range("A1:B7,A8:B10"), 2, 4)
。这种行为是我所要求的,但它可能不是你所期望的。
为了避免这种情况,您可以使用内置的Application.Union或Application.Intersect方法。如果可能,这些会自动折叠连续区域。以下所有工作:
' Every statement will print "A1:B10" - the areas are merged
' Union of separate areas
Debug.Print Union(Range("A1:B7"), Range("A8:B10")).Address
' Union of range with a known subrange
Debug.Print Union(Range("A1:B7,A8:B10"), Range("A1:B7,A8:B10").Cells(1, 1)).Address
' Union of range with itself
Debug.Print Union(Range("A1:B7,A8:B10"), Range("A1:B7,A8:B10")).Address
' Intersect of range with itself
Debug.Print Intersect(Range("A1:B7,A8:B10"), Range("A1:B7,A8:B10")).Address
如果这是索引时所需的行为,则在调用ContiguousIndex
之前执行列出的合并之一。请注意,如果区域在联合操作中未合并,则它们的相对不连续索引保持不变。 E.g。
' Yields "A:A,F:F,C:D" not "A:A,C:D,F:F" as you might desire
Debug.Print Union(Range("A:A,F:F,C:C,D:D"), Range("A:A,F:F,C:C,D:D")).Address
答案 2 :(得分:2)
我认为在看到你的例子之后我会更好地理解你的问题。它可以简化&#34;枚举列而不是范围:
Public Function ContiguousIndex(r As Range, row As Long, col As Long) As Range
Dim column As Range
For Each column In r.Columns
If col > 1 Then
col = col - 1
ElseIf col = 1 Then
If row <= column.Rows.Count And row > 0 Then
Set ContiguousIndex = column.Rows(row)
Exit Function
End If
Err.Raise vbObjectError + 9, , "Row Index out of bounds"
ElseIf col < 1 Then
Err.Raise vbObjectError + 9, , "Column Index out of bounds"
End If
Next
End Function
我找不到直接访问枚举器的方法(例如
r.Columns.[_NewEnum].Item(col)
不起作用)
<强>更新强>
仅举例来说
Public Function veryContiguousIndex(r As Range, row As Long, col As Long) As Range
Dim cell As Range, i As Long: i = col * row
For Each cell In r.Cells
If i = 1 Then Set veryContiguousIndex = cell: Exit Function
i = i - 1
Next
End Function
然后
Dim r As Range: Set r = [A1:B7,A8:B10]
Debug.Print r.Cells.Count; r.Columns.Count; r.Rows.Count ' 20 2 7
Debug.Print veryContiguousIndex(r , 9, 2).Address(0, 0) ' B9
Debug.Print veryContiguousIndex(r.EntireColumn, 9, 2).Address(0, 0) ' B9
Debug.Print veryContiguousIndex(r.EntireRow , 9, 2).Address(0, 0) ' R1
答案 3 :(得分:0)
怎么样:
v = myRange.Areas(2).Rows(5).Value 'retrieves the value in cell B5
只要每个子范围是一列,这似乎适用于原始和奖励问题。您还可以在VBA中创建一个简单的包装函数ContiguousIndex(Row,Column)
,以提供您描述的接口。
希望有所帮助。