在VBA中组合多个阵列

时间:2018-07-18 15:14:12

标签: arrays excel excel-vba

我目前正在尝试将46个阵列合并为一个阵列。我搜寻了互联网,但没有任何结果,希望这里有人能提供帮助。我确实找到了下面的页面,但是我需要能够在嵌套的for循环中浏览新数组的每个元素,因此使用下面的方法并不能完全达到我的最终目标。

Excel vba - combine multiple arrays into one

基本上,我需要以这样一种方式组合我的46个数组,以便可以使用嵌套的for循环遍历每个元素。即。

一组数组:

myArray1 = (1, 2, 3, 4)
myArray2 = (5, 6, 7)
myArray3 = (8, 9)
myArray4 = (10, 11, 12, 13, 14)
.
.
.
myArray46 = (101, 102, 103)

合并它们以形成新数组:

myNewArray = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14... 101, 102, 103)

在嵌套的for循环中遍历,以对照我的主数组检查每个元素:

For i = LBound(mainArray) to UBound(mainArray)
    For j = LBound(myArray) to UBound(myArray)

    If mainArray(i) = myArray(j) Then
    'do something
    End If

    Next j
Next i

对此非常感谢!

6 个答案:

答案 0 :(得分:4)

由于您在评论中写道,最终目标是创建一个唯一元素数组,因此最好使用字典,在字典中添加每个元素时可以测试其唯一性。像这样:

Option Explicit
Function uniqueArr(ParamArray myArr() As Variant) As Variant()
    Dim dict As Object
    Dim V As Variant, W As Variant
    Dim I As Long

Set dict = CreateObject("Scripting.Dictionary")
For Each V In myArr 'loop through each myArr
    For Each W In V 'loop through the contents of each myArr
        If Not dict.exists(W) Then dict.Add W, W
    Next W
Next V


uniqueArr = dict.keys

End Function

Sub tester()
    Dim myArray1, myArray2, myArray3, myArray4, myArray5
    myArray1 = Array(1, 2, 3, 4)
    myArray2 = Array(5, 6, 7, 8)
    myArray3 = Array(9, 10, 11, 12, 13, 14)
    myArray4 = Array(15, 16)
    myArray5 = Array(1, 3, 25, 100)

Dim mainArray

mainArray = uniqueArr(myArray1, myArray2, myArray3, myArray4, myArray5)

End Sub

如果运行Tester,则会看到mainArray包含:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
25
100

答案 1 :(得分:2)

使用数据,这是从多个数组中创建一个数组的方法:

Public Sub TestMe()

    Dim myA, myB, myC, myD, myE
    myA = Array(1, 2, 3, 4)
    myB = Array(5, 6, 7)
    myC = Array(8, 9)
    myD = Array(10, 11, 12, 13, 14)
    myE = Array(101, 102, 103)

    Dim myCombine As Variant
    Dim myNew() As Variant

    Dim myElement As Variant
    Dim myArr As Variant
    Dim cnt As Long

    myCombine = Array(myA, myB, myC, myD, myE)

    For Each myArr In myCombine
        For Each myElement In myArr
            ReDim Preserve myNew(cnt)
            myNew(cnt) = myElement
            cnt = cnt + 1
        Next
    Next

    For cnt = LBound(myNew) To UBound(myNew)
        Debug.Print myNew(cnt)
    Next cnt

End Sub

通过ReDim Preserve促进了新数组的“构建”,每当数组尺寸发生变化时,旧值就会保留在数组中。而且,如果您想对这些数组进行操作,则可以使用3个嵌套循环(有点慢)并进行一些检查:

Dim cnt2 As Long
For cnt = LBound(myNew) To UBound(myNew)
    For cnt2 = LBound(myCombine) To UBound(myCombine)
        For Each myElement In myCombine(cnt2)
            If myElement = myNew(cnt) Then
                Debug.Print myElement & vbTab & " from " & vbTab & cnt2
            End If
        Next myElement
    Next cnt2
Next cnt

这是您在直接窗口中看到的:

1    from   0
2    from   0
3    from   0
4    from   0
5    from   1
6    from   1
7    from   1
8    from   2
9    from   2
10   from   3
11   from   3
12   from   3
13   from   3
14   from   3
101  from   4
102  from   4
103  from   4

答案 2 :(得分:1)

“逐个砖块”的替代方法。

Option Explicit

Sub combineArrays()
    Dim myArray1 As Variant, myArray2 As Variant, myArray3 As Variant
    Dim myArray4 As Variant, myArray46 As Variant

    ReDim mainArray(0) As Variant

    myArray1 = Array(1, 2, 3, 4)
    myArray2 = Array(5, 6, 7)
    myArray3 = Array(8, 9)
    myArray4 = Array(10, 11, 12, 13, 14)
    '...
    myArray46 = Array(101, 102, 103)

    mainArray = buildMainArray(myArray1, mainArray)
    mainArray = buildMainArray(myArray2, mainArray)
    mainArray = buildMainArray(myArray3, mainArray)
    mainArray = buildMainArray(myArray4, mainArray)
    mainArray = buildMainArray(myArray46, mainArray)
    ReDim Preserve mainArray(UBound(mainArray) - 1)

    Debug.Print Join(mainArray, ",")

End Sub

Function buildMainArray(arr As Variant, marr As Variant)
    Dim i As Long

    For i = LBound(arr) To UBound(arr)
        marr(UBound(marr)) = arr(i)
        ReDim Preserve marr(UBound(marr) + 1)
    Next i

    buildMainArray = marr
End Function

答案 3 :(得分:1)

使用Redim Preserve合并数组的问题是它可能是expensive operation,因为基本上每次调用时都会重新创建数组。由于您要合并的数组有46个,因此很可能要等待一会儿。

相反,您可以遍历数组以找出所需元素的总数,确定主数组的尺寸,然后再次遍历数组以进行实际分配/合并。像这样:

  ' encapsulates code to determine length of an individual array
  ' note that because arrays can have different LBounds in VBA, we can't simply use
  ' Ubound to determine array length
  Public Function GetArrayLength(anArray As Variant) As Integer
     If Not IsArray(anArray) Then
        GetArrayLength = -1
     Else
        GetArrayLength = UBound(anArray) - LBound(anArray) + 1
     End If
  End Function

  Public Function CombineArrays(ParamArray arraysToMerge() As Variant) As Variant
     ' index for looping over the arraysToMerge array of arrays,
     ' and then each item in each array
     Dim i As Integer, j As Integer

     ' variable to store where we are in the combined array
     Dim combinedArrayIndex As Integer

     ' variable to hold the number of elements in the final combined array
     Dim CombinedArrayLength As Integer

     ' we don't initialize the array with an array-length until later,
     ' when we know how long it needs to be.
     Dim combinedArray() As Variant

     ' we have to loop over the arrays twice:
     ' First, to figure out the total number of elements in the combined array
     ' second, to actually assign the values
     ' otherwise, we'd be using Redim Preserve, which can get quite expensive
     ' because we're creating a new array everytime we use it.
     CombinedArrayLength = 0
     For i = LBound(arraysToMerge) To UBound(arraysToMerge)
        CombinedArrayLength = CombinedArrayLength + GetArrayLength(arraysToMerge(i))
     Next i

     ' now that we know how long the combined array has to be,
     ' we can properly initialize it.
     ' you can also use the commented code instead, if you prefer 1-based arrays.
     ReDim combinedArray(0 To CombinedArrayLength - 1)
     ' Redim combinedArray(1 to CombinedArrayLength)

     ' now that the combinedarray is set up to store all the values in the arrays,
     ' we can begin actual assignment
     combinedArrayIndex = LBound(combinedArray)
     For i = LBound(arraysToMerge) To UBound(arraysToMerge)
        For j = LBound(arraysToMerge(i)) To UBound(arraysToMerge(i))
           combinedArray(combinedArrayIndex) = arraysToMerge(i)(j)
           combinedArrayIndex = combinedArrayIndex + 1
        Next j
     Next i

     ' assign the function to the master array we've been using
     CombineArrays = combinedArray
  End Function

要使用此功能,您需要执行以下操作:

  Public Sub TestArrayMerge()
     Dim myArray1() As Variant
     Dim myArray2() As Variant
     Dim myArray3() As Variant
     Dim myArray4() As Variant
     Dim combinedArray As Variant

     myArray1 = Array(1, 2, 3, 4)
     myArray2 = Array(5, 6, 7)
     myArray3 = Array(8, 9)
     myArray4 = Array(10, 11, 12, 13, 14)

     combinedArray = CombineArrays(myArray1, myArray2, myArray3, myArray4)

     If IsArray(combinedArray) Then
        Debug.Print Join(combinedArray, ",")
     End If
  End Sub

关于最后一点,您正在使用内部循环来组合最终组合数组中的值:内部循环不需要从LBound(myArray)开始。对于i的任何值,您已经将它与它之前的元素进行了比较(例如,当i = 2时,它已经与第一个元素进行了比较)。所以您真的只需要:

    For i = LBound(combinedArray) To UBound(combinedArray) - 1
        For j = i + 1 To UBound(combinedArray)
           ' do whatever you need
        Next j
     Next i

答案 4 :(得分:0)

也许这...

'To determine if a multi-dimension array is allocated (or empty)
'Works for any-dimension arrays, even one-dimension arrays
Public Function isArrayAllocated(ByVal aArray As Variant) As Boolean

On Error Resume Next
isArrayAllocated = IsArray(aArray) And Not IsError(LBound(aArray, 1)) And LBound(aArray, 1) <= UBound(aArray, 1)
Err.Clear: On Error GoTo 0

End Function

    'To determine the number of items within any-dimension array
    'Returns 0 when array is empty, and -1 if there is an error
    Public Function itemsInArray(ByVal aArray As Variant) As Long
    Dim item As Variant, UBoundCount As Long

    UBoundCount = -1
    If IsArray(aArray) Then
        UBoundCount = 0
        If isArrayAllocated(aArray) Then
            For Each item In aArray
                UBoundCount = UBoundCount + 1
            Next item
        End If
    End If
    itemsInArray = UBoundCount

    End Function

        'To determine the number of dimensions of an array
        'Returns -1 if there is an error
        Public Function nbrDimensions(ByVal aArray As Variant) As Long
        Dim x As Long, tmpVal As Long

        If Not IsArray(aArray) Then
            nbrDimensions = -1
            Exit Function
        End If

        On Error GoTo finalDimension
        For x = 1 To 65536 'Maximum number of dimensions (size limit) for an array that will work with worksheets under Excel VBA
            tmpVal = LBound(aArray, x)
        Next x

        finalDimension:
        nbrDimensions = x - 1
        Err.Clear: On Error GoTo 0

        End Function

        '****************************************************************************************************
        ' To merge an indefinite number of one-dimension arrays together into a single one-dimension array
        ' Usage: mergeOneDimArrays(arr1, arr2, arr3, ...)
        ' Returns an empty array if there is an error
        ' Option Base 0
        '****************************************************************************************************
        Public Function mergeOneDimArrays(ParamArray infArrays() As Variant) As Variant
        Dim x As Long, y As Long, UBoundCount As Long, newUBoundCount As Long
        Dim tmpArr As Variant, allArraysOK As Boolean

        UBoundCount = 0
        allArraysOK = True
        For x = LBound(infArrays) To UBound(infArrays)
            If Not IsArray(infArrays(x)) Or Not nbrDimensions(infArrays(x)) = 1 Then
                allArraysOK = False
                Exit For
            End If
            UBoundCount = UBoundCount + itemsInArray(infArrays(x))
        Next x
        If allArraysOK Then
            ReDim tmpArr(0 To UBoundCount - 1)
            UBoundCount = 0
            For x = LBound(infArrays) To UBound(infArrays)
                For y = LBound(infArrays(x)) To UBound(infArrays(x))
                    tmpArr(UBoundCount) = infArrays(x)(y)
                    UBoundCount = UBoundCount + 1
                Next y
            Next x
            newUBoundCount = itemsInArray(tmpArr)
            If newUBoundCount = UBoundCount Then
                mergeOneDimArrays = tmpArr
            Else
                mergeOneDimArrays = Array()
            End If
            Erase tmpArr
        Else
            mergeOneDimArrays = Array()
        End If

        End Function

答案 5 :(得分:-1)

如果使用一维数组,则可以使用集合。它在处理动态大小调整方面要好得多。

您可以声明一个集合,然后将数组中的每个元素添加到其中。然后,您将有一个包含所有值的大列表。

Dim coll As New Collection
coll.Add MyArray(j)

以下是收藏介绍的好地方: https://excelmacromastery.com/excel-vba-collections/