问题 检查值是否是列表的成员


  • 我必须根据项目列表检查一条用户输入;如果输入位于项目列表中,则将流程指向一个方向。如果没有,请将流量指向另一个流量。
  • 这个清单是  工作表本身可见;它必须在代码下进行混淆。

我想到了两种策略:

  1. 声明为 enum 并检查输入是否属于此 enum虽然我不确定这个的语法 - 我是否需要初始化 enum 每次我想用它?
  2. 声明为数组并检查输入是否是此数组的一部分。

我想知道VBA哪个在效率和可读性方面更好?


2367
2018-01-27 10:54


起源

我猜你已经在txt文件或Excel中提供了列表? - 我会直接将它作为数组使用。像Mehow那样逐行建立字典并不实用 - brettdj
不,这份清单在一张纸上! - Evil Washing Machine
嗯,这是一个惊喜:)我仍然会运行将它们添加到一个带有数组的代码行,而不是为每个新项添加新的代码行 - brettdj


答案:


与.NET语言不同,VBA不会将Enum公开为文本。它严格来说是一个数字,没有 .ToString() 将公开Enum名称的方法。可以创建自己的 ToString() 方法并返回枚举的String表示形式。它也有可能 枚举枚举类型。虽然所有这些都是可以实现的,但我不建议这样做,因为对于这样的单个任务而言过于复杂。

如何创建项目的Dictionary集合并简单地使用 Exist 方法和某种错误处理(或简单的if / else语句)检查列表中是否存在输入框中的任何用户输入。

例如:

Sub Main()

    Dim myList As Object
    Set myList = CreateObject("Scripting.Dictionary")

    myList.Add "item1", 1
    myList.Add "item2", 2
    myList.Add "item3", 3

    Dim userInput As String
    userInput = InputBox("Type something:")

    If myList.Exists(userInput) Then
        MsgBox userInput & " exists in the list"
    Else
        MsgBox userInput & " does not exist in the list"
    End If

End Sub

注意:如果添加引用 Microsoft Scripting Runtime 然后你可以使用图书馆的智能 myList 对象,因为它本来是早期绑定替换

 Dim myList As Object
 Set myList = CreateObject("Scripting.Dictionary")

Dim myList as Dictionary
Set myList = new Dictionary

这取决于你想要采用哪种方式以及更方便的方式。请注意,如果您使用Late Binding,则不需要添加引用,如果您希望使用智能早期绑定,则需要引用。


只是为了让读者能够使用Enum可视化版本,让我演示这种机制可能如何工作

Enum EList
    item1
    item2
    item3
    [_Min] = item1
    [_Max] = item3
End Enum

Function ToString(eItem As EList) As String
    Select Case eItem
        Case EList.item1
            ToString = "item1"
        Case EList.item2
            ToString = "item2"
        Case EList.item3
            ToString = "item3"
    End Select
End Function

Function Exists(userInput As String) As Boolean
    Dim i As EList
    For i = EList.[_Min] To EList.[_Max]
        If userInput = ToString(i) Then
            Exists = True
            Exit Function
        End If
    Next
    Exists = False
End Function

Sub Main()

    Dim userInput As String
    userInput = InputBox("type something:")

    MsgBox Exists(userInput)

End Sub

首先你宣布你的 名单 作为Enum。我只为示例添加了3个项目,以尽可能简单。 [_Min] 和 [_Max] 表示枚举的最小值和最大值(有可能再调整一下,让我们现在保持简单)。你声明它们都能够迭代你的 EList

ToString() method返回Enum的String表示形式。任何VBA开发人员都会在某种程度上意识到VBA太糟糕了,因为它是一个内置功能。无论如何,你现在有了自己的实现。

Exists 拿什么 userInput 存储并迭代Enum EList 匹配Enum的String表示形式。这是一种矫枉过正,因为你需要调用许多方法并循环遍历枚举才能实现简单 DictionaryExists 方法一气呵成。这主要是为什么我不建议您使用Enums来解决您的具体问题。

然后你最终得到了 Mainsub只是收集用户的输入并调用 Exists 方法。它显示了一个消息框 true 要么 false 表示String是否作为枚举类型存在。


6
2018-01-27 11:14



mehow,我错过了什么,或者你的 DoesntExist: 很奇怪?如果字典中不存在用户输入, myList.Exists(userInput) 回来 false 并且没有触发错误 - Dmitry Pavliv
你是对的@simoco我没有在发布之前测试过代码而忽略了这一点。已经用更简单的方法更新了答案
@mehow On msdn.microsoft.com/en-us/library/twsk0311.aspx 似乎表明枚举可以是字符串,不是吗? - Evil Washing Machine
@SchwitJanwityanujit你正在看Visual Basic引用而不是VBA的不同。
@SchwitJanwityanujit experts-exchange.com/Software/Office_Productivity/Office_Suites/...


您可以运行一个简单的数组测试,如下所示:将单词添加到单个列表中:

Sub Main1()
arrList = Array("cat", "dog", "dogfish", "mouse")
Debug.Print "dog", Test("dog")   'True
Debug.Print "horse", Test("horse") 'False
End Sub

Function Test(strIn As String) As Boolean
Test = Not (IsError(Application.Match(strIn, arrList, 0)))
End Function

或者,如果您想进行更详细的搜索并返回子字符串匹配列表以供进一步工作,请使用 Filter。此代码将返回以下内容 vFilter 如果抬头 dog

狗,鲨鱼

在这种特殊情况下,代码然后检查完​​全匹配 dog

Sub Main2()
arrList = Array("cat", "dog", "dogfish", "mouse")
Debug.Print "dog", Test1("dog")
Debug.Print "horse", Test1("horse")
End Sub

Function Test1(strIn As String) As Boolean
Dim vFilter
Dim lngCnt As Long
vFilter = Filter(arrList, strIn, True)
For lngCnt = 0 To UBound(vFilter)
    If vFilter(lngCnt) = strIn Then
        Test1 = True
        Exit For
    End If
Next
End Function

6
2018-01-30 10:19



很好的答案,但为时已晚 - 我已经手动将大约100个项目添加到字典中:(。当然,除了所有的手动工作之外,解决方案并没有出现任何问题。 - Evil Washing Machine
将它们存储在数组中的好主意。对代码稍作修改就是将列表存储在带分隔符的变量中,然后使用 Split 创建你的数组 - Siddharth Rout
很抱歉恢复这个旧帖子,但我想知道哪种方法(字典和数组之间)最有效,更快。还有阵列,正在使用 Application.Match 比循环遍历数组更快?谢谢 - Oscar Anthony
为了回答我自己的问题,我发现使用了 Match() 函数实际上比通过数组迭代(循环)慢得多。我使用大小为2000的数组进行了测试。循环遍历数组的最坏情况是寻找最后一项(在索引2000处)。经过5000次呼叫后 Match() 功能和循环,总时间 Match() 是 3.746094 但只有 1.667969 用于循环遍历数组。 检查此问题的答案,查看我在测试中使用的代码 - Oscar Anthony


只需使用 Select Case 列表:

Select Case entry
   Case item1,item2, ite3,item4 ' add up to limit for Case, add more Case if limit exceeded
      do stuff for being in the list
   Case Else
      do stuff for not being in list
End Select

-1
2017-08-22 15:00



这不能解决问题。要批评或要求作者澄清,请在帖子下方留言 - 您可以随时评论自己的帖子,一旦有足够的评论 声誉 你将能够 评论任何帖子。 - Sufian