【问题标题】:Checking if a value is a member of a list检查一个值是否是列表的成员
【发布时间】:2014-02-18 04:19:35
【问题描述】:
  • 我必须根据项目列表检查用户输入;如果输入在项目列表中,则以一种方式引导流程。如果没有,将流程引导到另一个。
  • 此列表在工作表本身上可见;它必须在代码下进行混淆。

我已经想到了两种策略来做到这一点:

  1. 声明为enum 并检查输入是否是此enum 的一部分,尽管我不确定它的语法 - 每次我想使用它时是否需要初始化enum
  2. 声明为数组并检查输入是否是该数组的一部分。

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

【问题讨论】:

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

标签: vba excel


【解决方案1】:

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

您如何创建项目的 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

这取决于您想采用哪种方式以及哪种方式更方便。请注意,如果您使用后期绑定,则不需要添加引用,而如果您希望使用智能感知进行早期绑定,则需要引用。


为了让读者能够使用 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

首先您将您的 List 声明为 Enum。为了使示例尽可能简单,我只添加了 3 个项目。 [_Min][_Max] 表示 enum 的最小值和最大值(可以调整它,但同样,让我们​​暂时保持简单)。您声明它们都能够迭代您的EList

ToString() 方法返回 Enum 的字符串表示形式。任何 VBA 开发人员都会在某些时候意识到 VBA 缺少作为内置功能的这一点太糟糕了。无论如何,您现在已经有了自己的实现。

Exists 采用 userInput 存储的任何内容,并在迭代枚举时 EList 匹配您的枚举的字符串表示形式。这是一种矫枉过正,因为您需要调用许多方法并遍历枚举,以便能够一次性实现简单的DictionaryExists 方法所做的事情。这主要是为什么我不建议针对您的特定问题使用 Enums。

最后你得到Main sub,它简单地收集来自用户的输入并调用Exists 方法。它显示一个带有 truefalse 的消息框,指示字符串是否作为 Enum 类型存在。

【讨论】:

  • mehow,我错过了什么,还是你的DoesntExist: 很奇怪?如果字典中不存在用户输入,myList.Exists(userInput) 只会返回false 并且不会触发错误
  • 你说得对@simoco 我在发布之前没有测试过代码并忽略了这一点。已经用更简单的方法更新了答案
  • @mehow On msdn.microsoft.com/en-us/library/twsk0311.aspx 似乎暗示枚举可以是字符串,不是吗?
  • @SchwitJanwityanujit 您正在查看 Visual Basic 参考而不是 VBA,它们是不同的。
【解决方案2】:

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

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。如果查找dog,此代码将通过vFilter 返回以下内容

狗,狗鱼

在这种特殊情况下,代码会检查 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

【讨论】:

  • 很好的答案,但为时已晚-我已经手动将大约 100 个项目添加到字典中 :(。当然,除了所有手动工作之外,该解决方案没有任何问题.
  • 将它们存储在数组中的好主意。对代码稍作修改是将列表存储在一个带分隔符的变量中,然后使用Split 创建您的数组
  • 抱歉恢复这篇旧帖子,但我想知道哪种方法(在字典和数组之间)最有效、更快。同样对于数组,使用Application.Match 是否比遍历数组更快?谢谢
  • 为了回答我自己的问题,我发现使用Match() 函数实际上比仅遍历(循环)数组要慢得多。我对一个大小为 2000 的数组进行了测试。循环遍历数组的最坏情况是寻找最后一项(在索引 2000 处)。在对 Match() 函数和循环进行 5000 次调用后,Match() 的总时间为 3.746094,但只有 1.667969 用于循环遍历数组。 Check the answer to this question for the code I used in my testing
【解决方案3】:

只需将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

【讨论】:

  • 这没有提供问题的答案。要批评或要求作者澄清,请在他们的帖子下方发表评论 - 您可以随时评论自己的帖子,一旦您有足够的reputation,您就可以comment on any post
猜你喜欢
  • 1970-01-01
  • 2020-12-19
  • 2014-07-17
  • 1970-01-01
  • 1970-01-01
  • 2018-02-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多