具有多个条件的索引/匹配函数查找数组

时间:2014-08-08 19:04:59

标签: excel vba excel-vba

我看了,但找不到针对这种特殊情况的有效回复。

我需要编写一个宏,在表格中搜索特定名称(列A),材料(列B)和颜色(列C)组合,然后从列D返回价格。

我可以输入公式(数组)

=INDEX(Sht1!A1:D5552,MATCH(1,(Sht1!A1:A5552=A1)*(Sht1!B1:B5552=B1)*(Sht1!G1:G5552=C1),0),4)

其中A1具有我正在搜索的项目的名称,B1具有材质,C1具有颜色,并且值返回正常。但是,我已将其设置为快速选择名称/材质/颜色,我希望能够快速提取价格,所以当我点击按钮时,我试图编写一个宏来运行。

我遇到的问题是转置MATCH(1,(Sht1!A1:A5552 = A1)(Sht1!B1:B5552 = B1)(Sht1!C1:C5552 = C1), 0)部分进入VBA。我试图避免使用记录功能,因为它以R1C1格式吐出.ForumlaArray,以后很难解释和更新。

我试过了

Application.Index(Sht1.Range("A1:D5552"), _
  Application.Match(1, Sht1.Range("A1:A5552") = Range("A1") & _
  "*" & Sht1.Range("B1:B5552") = Range("B1") & "*" & Sht1.Range("C1:C5552") = _
  Range("C1"), 0), 4)

但是获得类型不匹配

我也试过放入.ForumlaArray但得到“无法设置范围类的FormulaArray属性”错误(因为它需要R1C1格式)

"=INDEX(Sht1!A1:D5552,MATCH(1,(Sht1!A1:A5552=A1)*(Sht1!B1:B5552=B1)*(Sht1!C1:C5552=C1),0),4)"

我的变量设置略有不同,但我试图简化这些示例,以便更容易理解。

2 个答案:

答案 0 :(得分:1)

我认为用完全不同的方法会更好。我不是让VBA进行查找,而是使用VBA来维护表列中的唯一值列表,每个列都包含在命名范围内。然后使用数据验证为VBA创建的命名范围填充的每个搜索项(名称,材料,颜色)创建下拉菜单,并继续使用上面显示的数组公式来执行实际查找。

参见Jean-FrançoisCorbett关于如何Populate unique values in to VBA array from excel的答案。

使用Tablestructured references相结合,您可以在Table的底部添加更多条目,并自动扩展以包含它们。设置您的VBA代码以在NamesList更改时构建新的唯一值列表(并将其存储在命名范围中,例如MaterialsListColorListTable)。

另一种选择是简单地使用数据透视表。

编辑:Here's another nice approach来构建Range的唯一值列表。

答案 1 :(得分:1)

您错误地使用了Application.Match函数和VBA的一些基础知识。简而言之,该功能不像公式那样有效。

AFAIK Application.Match获取要查找的值,并查找1D范围的单元格并返回数组中单元格的索引。所以听起来你需要多次为每一列做这件事。当然,我找不到任何acceptable documentation

正确的用法是:

row_index = Application.Match(Sht1.Range("A1").Value, Sht1.Range("A1:A5552"), 0)
price_value = Sht1.Cells(row_index, "D").Value

但这只会返回第一场比赛,而不是所有比赛。如果您所做的只是在VBA中复制公式,VBA不会提高性能。其他海报是正确的,可能需要采用不同的方法。


你遇到的其他一些基本原则主要来自于你采用公式字符串并尝试将其直接翻译成Application.Match你无法做到的AFAIK。

您正在尝试指定何时匹配的条件:

Sht1.Range("A1:A5552") = Range("A1")

你真正在做的是尝试比较范围。 VBA在将其传递给Application.Match之前尝试将其作为布尔值进行评估。这将返回类型错误,因为范围具有不同的维度。

如果您还没有这样做:Sht1需要声明为Worksheet对象并设置为ActiveWorkbook.Sheets("Sht1")

Dim Sht1 As WorkSheet
Set Sht1 = ActiveWorkbook.Sheets("Sht1")

您不能仅仅希望变量Sht1已经引用了工作表" Sht1"。

然后你试着把这些串起来。

Sht1.Range("A1:A5552") = Range("A1") & "*" & _
Sht1.Range("B1:B5552") = Range("B1") & "*" & _
Sht1.Range("C1:C5552") = Range("C1")

我认为您理解&用于字符串连接但是您尝试连接,字符串"*"与未定义的布尔值/范围。

VBA解决方案

如果您想要使用VBA的解决方案,您可以遍历所有行,直到找到匹配项:

Function FindProduct(name As String, material As String, color As String) As String

    Dim product_sheet As WorkSheet
    Set product_sheet = GetProductSheet() ' Workbooks(BOOK_NAME).Sheets(SHEET_NAME)

    Dim row_index as Long
    For row_index = 2 To product_sheet.UsedRange.Rows.Count

        If product_sheet.Cells(row_index, NAME_COL).Value = name _
        And product_sheet.Cells(row_index, MATERIAL_COL).Value = material _
        And product_sheet.Cells(row_index, COLOR_COL).Value = color Then

            FindProduct = product_sheet.Cells(row_index, PRODUCT_COL).Value
            Exit Function

        End If

    Next row_index

End Function

或者性能是一个问题:

  • 保持您的数据按照列排列" A"," B"," C" (例如,按此顺序。)
  • 在列" A"
  • 中查找匹配的行范围
  • 从该范围中,找到列" B"
  • 中匹配的子行范围
  • 从该范围返回列#34; C"
  • 中匹配的子行范围

我可能会在稍后提供。