索引到不连续的大范围

假设我有一个很大的不连续范围,可能是Range("B:B,E:E,F:F") 。 我怎么去索引这个范围来把它看作是连续的。

例如,我想要做类似的事情

 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,但似乎都performance出同样的溢出效应。

我想到的另一种方法是将值分配给一个variables数组,并从那里手动索引,但这很快就会变得复杂,因为一个简单的片段

 Dim v() As Variant v = myRange 

只会将不连续区域的第一个区域分配到数组中,留下一个(20 ^ 20-1)x1数组,并完全忽略myRange的其余部分。 所以如果我循环遍历所有区域并将它们单独分配给一个数组,然后将其重新分配给一个数组,那么将整个myRange放入一个数组中可能是可行的,但是这样做并不容易,而且我得到的数组使用的内存比我想要的要多得多(除非我把更多的开销放在修剪下来,或者我随意select更less的行数来复制)。

在这一点上,只要手动循环遍历这些区域,并且自己做索引,而不需要将所有东西放到一个数组中,这样做效率和简单得多。 这最后的方法是我目前正在做的。

问题

是否有任何现有的方法或技巧,我可以用它来对待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的值? 这是我正在使用的方法的属性,我很乐意继续。

再一次,我已经有了这个function,但是我猜测,在Excel的所有怪癖中隐藏着某种奇妙的方法或者技巧,那将会更好。

我会发布我自己的解决scheme,以防其他人遇到类似的问题。 这是我唯一的工作,因为其他的答案和评论依赖于了解范围内的区域(例如依靠每个区域是一个单一的列,我不能保证,因为我的范围是用户友好的,input并可能跨越多列或有限数量的行)。

 ' 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 

值得注意的是我在评论中提到的东西,但可能是意料之外的:这段代码将顶部alignment所有区域,使得区域1中的第一行与区域2中的第一行处于相同的索引处是相同的…等等当调用像ContiguousIndex(Range("A1:B7,A8:B10"), 9, 2) 9,2)时会导致一个怪癖。 虽然看起来很明显这应该返回B9 ,但事实并非如此 – 它实际上会尝试访问A1:B7的第9行第2列,导致错误。 这是因为两个不连续的范围,尽pipe它们在实际表格上从头到尾排列得很清楚,但它们被视为是并排的。 所以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之前执行所列出的合并之一。 请注意,如果工会活动中的地区没有参与,则其相对不连贯的指数保持不变。 例如

 ' 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 

需要注意的是,使用.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"

看到你的例子之后,我想我更了解你的问题。 它可以通过枚举列而不是范围来“简化”一点:

 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 

怎么样:

 v = myRange.Areas(2).Rows(5).Value 'retrieves the value in cell B5 

只要每个子范围是单个列,这似乎适用于原始和奖金问题。 你也可以在VBA中创build一个简单的包装函数ContiguousIndex(Row,Column)来给你描述的接口。

希望有所帮助。