我在Excel中有一个表,其中的列标题与工作簿中其他地方的动态命名范围的一部分相对应。例如,我有这些列标题:"10"、"20"等,以及这些动态命名的范围:"ExampleRange10"one_answers"ExampleRange2"等。我想通过连接字符串"ExampleRange"和列标题"10"来输入一个引用ExampleRange 10的VLookup公式。这将允许我简单地将公式扩展到表中的所有列,而不是在每列的公式中手动键入"ExampleRange10"、"ExampleRange20"等。
我知道INDIRECT函数,过去曾成功地将其用于命名范围,但在这种情况下,它似乎无法用于动态命名范围。我猜这是一个细微之处,与Excel如何定义动态命名范围有关(例如,它们不会显示在公式栏左侧的命名范围下拉列表中,并且在VBA中有一些有趣的属性)。有没有一种方法可以将INDIRECT公式与动态命名范围结合使用,或者有没有其他方法可以解决这个问题?
编辑:以下是使用的确切公式
这是主公式:=VLOOKUP(B2,INDIRECT("ExampleRange"&C1),2,FALSE)
,其中C1包含"10",我的动态命名范围"ExampleRange10"的公式是:=OFFSET(Sheet1!$F$2,0,0,COUNTA(Sheet1!$F$2:$F$25),2)
。主公式返回"#REF!",但当我删除动态命名范围公式并简单地将"ExampleRange10"定义为静态范围时,它可以正常工作。
经过进一步研究,我可以说,Excel的INDIRECT
函数根本不适用于动态范围。也许有一种聪明的方法可以绕过使用INDIRECT
并坚持使用非VBA Excel世界,但我不知道有这样的方法。相反,我最终创建了一个用户定义的函数,与这里描述的函数非常相似。我将主公式改为=VLOOKUP(B2,DINDIRECT("ExampleRange"&C1),2,FALSE)
,其中DINDIRECT
是我创建的VBA函数的名称。
这种替代方案的唯一缺点(根据您的看法,可能是缺点,也可能不是缺点)是工作簿必须保存为启用宏的工作簿,并且自定义函数的使用不是很自我记录,需要向其他用户进行一些解释。不过,综合考虑,这对我来说是一个可以接受的解决方案
对于链接厌恶者,以下是代码:
Public Function DINDIRECT(sName As String) As Range
Dim nName As Name
On Error Resume Next
Set nName = ActiveWorkbook.Names(sName)
Set nName = ActiveSheet.Names(sName)
On Error GoTo 0
If Not nName Is Nothing Then
Set DINDIRECT = nName.RefersToRange
Else
DINDIRECT = CVErr(xlErrName)
End Function
注意:虽然这个解决方案有效,但我不会接受我的答案,因为我不想阻止其他人发布更好的解决方案。此外,我是这个网站的新手,如果我回答自己的问题违反了任何礼仪规范,我很抱歉。。。我只是想分享我使用的确切解决方案,以防其他人发现它有用。
我最近遇到了这堵砖墙,正如你已经猜到的那样,答案只是你不能用INDIRECT引用动态命名范围。
然而,你可以使用动态范围公式本身作为INDIRECT的参数,但这对你想做的事情没有用。有点像PITA,因为它是一种非常有用的功能。
如果您的数据具有10、20等标头,则不需要使用Indirect。为什么不直接使用"索引/匹配"来选择您需要的数据?
例如,将整个表命名为ExampleRanges,并使用以下公式:
Index(ExampleRanges, match(B2, index(ExampleRanges, , 1), 0), match(C1, index(ExampleRanges, 1,), 0))
我知道这已经很老了,但我只是刚刚遇到这个问题,并认为我应该添加一个避免任何VBA编码的解决方案,以防它帮助其他遇到这个问题的人:
=VLOOKUP(B2,CHOOSE(C1/10,example10,example20,example30,example40),2,0)
这是假设命名约定为10,20,30等,对于数百个范围来说并不理想。
未测试,但我认为这会起作用:
用户定义的函数,返回动态命名范围的地址:
Function Named_Range_Address(Range_Name As Range, _
Optional SheetName As Boolean) As String
Dim strName As String
Application.Volatile
If SheetName = True Then
strName = "'" & Range_Name.Parent.Name & "'!" & Range_Name.Address
Else
strName = Range_Name.Address
End If
Named_Range_Address = strName
End Function
那么你应该能够使用你的vlookup公式:
=VLOOKUP(B2,INDIRECT(named_range_address("ExampleRange"&C1,TRUE)),2,FALSE)
今天我在修改Excel命名范围,我发现,虽然你不能在INDIRECT()
调用本身中计算范围的名称,但你仍然可以通过添加一个中间步骤以纯"Excel方式"获得它:只需创建一些隐藏的单元格,在其中计算命名范围。
例如,假设A1
包含范围名称的"动态部分",那么在A2
中使用公式= "ExampleRange" & A1
,现在就有了完整的范围名称,可以将其用作= INDIRECT(A2)
。
添加一个新的扭曲,可以将命名范围与Address和Indirect函数一起使用。我有一个例子,我正在为一系列表设置命名范围,并使用以下内容:
Named Range: WWDH-FF-PI which points to Linear!$A$19 (first cell in table)
获取地址:$T$56:=地址(匹配(S56,线性!A:A,0),1,1,1,"线性")
然后使用多次复制的偏移函数创建一个数据透视表:
=OFFSET(INDIRECT($T$56),C5,$T$57-1)
因此,Address函数可以嵌入(或封装)到Indirect函数中,以创建动态单元格地址。
我知道这是一个非常古老的线程,但我也有同样的问题,所以也许我的解决方案可以在未来帮助人们。
基本上,我创建了一个宏,它将在保存时删除并重新定义范围,并为其命名。因此,INDIRECT函数将起作用,因为范围不是动态的。您所需要做的就是在将任何值添加到命名范围后保存工作簿
Private Sub Workbook_BeforeSave(ByVal SaveAsUI As Boolean, Cancel As Boolean)
Dim a, b, c, d, e, f As Integer
Dim data As Worksheet
Set data = ThisWorkbook.Worksheets("Data")
a = data.Range("A" & Rows.count).End(xlUp).row
b = data.Range("B" & Rows.count).End(xlUp).row
c = data.Range("C" & Rows.count).End(xlUp).row
d = data.Range("D" & Rows.count).End(xlUp).row
e = data.Range("E" & Rows.count).End(xlUp).row
f = data.Range("F" & Rows.count).End(xlUp).row
ActiveWorkbook.Names("KP").Delete
ActiveWorkbook.Names("KPT").Delete
ActiveWorkbook.Names("AP").Delete
ActiveWorkbook.Names("APT").Delete
ActiveWorkbook.Names("DISC").Delete
ActiveWorkbook.Names("SEATS").Delete
ActiveWorkbook.Names.Add Name:="KP", RefersTo:="=Data!$A$2:$A$" & a
ActiveWorkbook.Names.Add Name:="KPT", RefersTo:="=Data!$B$2:$B$" & b
ActiveWorkbook.Names.Add Name:="AP", RefersTo:="=Data!$C$2:$C$" & c
ActiveWorkbook.Names.Add Name:="APT", RefersTo:="=Data!$D$2:$D$" & d
ActiveWorkbook.Names.Add Name:="DISC", RefersTo:="=Data!$E$2:$E$" & e
ActiveWorkbook.Names.Add Name:="SEATS", RefersTo:="=Data!$F$2:$F$" & f
End Sub