【问题标题】:VBA Dictionary with Dynamic Arrays带有动态数组的 VBA 字典
【发布时间】:2014-12-11 00:11:02
【问题描述】:

我正在尝试创建一个包含动态数组的动态字典。

电子表格中的示例行:

Facility Name|Contact Name|Contact Role

设施和联系人之间的关系是 M2M。我想重新创建一个如下所示的工作表:

Contact Name| Facility1 - role, Facility2 - role

我想做的是创建一个名称字典,其中唯一的名称用作键

New Dictionary  Names(name)

Names(name) 的值将是该名称出现的所有行号的数组。例如,假设“Joe Rose”出现在第 3、7 和 9 行:

names("Joe Rose") = [3,7,9]

我知道如何在 JS、Python、PHP 中做到这一点,但 VBA 让我发疯!

这是我目前得到的结果:

Dim names As Dictionary
Set names = New Dictionary

Dim name

For i=1 To WorkSheets("Sheet1").Rows.Count
  name = WorkSheets("Sheet1").Cells(i,2)
  If Not names(name) Then
    names(name) = i
  Else
    'help!
    'names(name)) push new i, maybe something with redim preserve?
  End If
Next i

即使只是将我指向一些我可以参考的文章也会很棒!来自 PHP 背景的 VBA 真是令人沮丧!

谢谢

【问题讨论】:

    标签: arrays vba excel dictionary multidimensional-array


    【解决方案1】:

    这有点棘手,因为您必须将数组从字典中拉出来才能使用它,然后再放回去:

    Sub Tester()
    
        Dim names As Dictionary
        Set names = New Dictionary
    
        Dim name, tmp, ub, i, k
    
        For i = 1 To Worksheets("Sheet1").UsedRange.Rows.Count
    
            name = Trim(Worksheets("Sheet1").Cells(i, 2).Value)
    
            If Len(name) > 0 Then
                If Not names.Exists(name) Then
                    names(name) = Array(i)
                Else
                    tmp = names(name)
                    ub = UBound(tmp) + 1
                    ReDim Preserve tmp(0 To ub)
                    tmp(ub) = i
                    names(name) = tmp
                End If
            End If
        Next i
    
        For Each k In names.Keys
            Debug.Print k, Join(names(k), ",")
        Next k
    
    
    End Sub
    

    【讨论】:

    • 这是我一直想采用的方法,但我还没有习惯重新定义数组的大小(我只是习惯于“推入”一个值!)
    • 是的 - VBA 中没有 push() ;-(
    【解决方案2】:

    让我们这样做。首先将字典的值构建为逗号分隔的字符串。然后,如果您需要/想要,您可以使用SPLIT 函数将其转换为数组。

    Dim names As Dictionary
    Set names = New Dictionary
    
    Dim name
    
    For i = 1 To WorkSheets("Sheet1").Rows.Count
      name = WorkSheets("Sheet1").Cells(i,2)
    
      If names.Exists(name) Then
          names(name) = names(name) & "," & i
      Else
          names(name) = i
      Next
    
    Next i
    
    For each name in names
        names(name) = Split(name, ",")
    Next
    

    【讨论】:

    • 这是个好主意 - 使它成为一个易于拆分的字符串。正如我刚刚读到的,Split 函数会将字符串作为数组返回。谢谢大卫!
    • 虽然蒂姆的回答最终回答了这个问题,但我认为你先把它放在一个字符串中然后再拆分的想法很聪明。不幸的是,由于我正在处理超过 200k 行,分割字符串的额外循环导致 excel 挂起!我对此感到有些惊讶,因为他的解决方案需要创建一个新数组并经常在循环中间重新调整尺寸,但解决方案出奇地快。嗯……
    • 我没有对几乎那么大的任何数据进行测试。仍然很奇怪......因为第一个循环总是为每一行迭代一次,(特别是在蒂姆的回答的情况下,我一直明白ReDim Preserve 是一项昂贵的操作,通常应该避免 within 循环,如果可能的话)而第二个循环只对字典中的每个Key 进行迭代,它应该更小。也许Split 函数在内存使用方面很昂贵,但是...
    【解决方案3】:

    循环时尽量避免使用[worksheet].rows.count,excel 2010的值超过100万。

    Public Sub test()
        Dim names As Dictionary
        Dim name
        Dim cell As Object
    
        'finds last row in column 2
        lastRow = Worksheets("Sheet1").Cells(Rows.Count, 2).End(xlUp).Row
        Set names = New Dictionary
    
        For Row = 1 To lastRow
            Set cell = Worksheets("Sheet1").Cells(Row, 2)
            name = Split(cell.Text, "|")(0)
    
            If names.Exists(name) Then
                names(name) = names(name) & ", " & Row
            Else
                names.Add name, Row
            End If
        Next Row
    End Sub
    

    【讨论】:

    • 您对不使用 rows.count 提出了一个很好的观点。我想这在蒂姆的回答中解决了“UsedRange.Rows.Count”?
    • 是的,这是一个很好的解决方案。我认为是他向我指出,如果已格式化,UsedRange 将包含空单元格。有趣的是我们这次使用了相反的方法
    • 只是一个考虑因素,但我认为 David Zemens 使用字符串而不是数组是一个好主意。如果您想取回数组,只需使用 split(names, ", ")。重新调整数组似乎还有很多工作(哎呀,您已经指出了这一点)
    • 第二个注意事项是,在使用它们之前,您不需要拆分字符串(无需创建新循环来拆分它们)
    猜你喜欢
    • 1970-01-01
    • 2018-12-04
    • 2023-02-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-01-12
    • 2017-06-05
    • 1970-01-01
    相关资源
    最近更新 更多