Объединение нескольких массивов в VBA

В настоящее время я пытаюсь объединить 46 массивов в один массив. Я просмотрел Интернет, но безуспешно, и надеюсь, что кто-то здесь может помочь. Я нашел приведенную ниже страницу, но мне нужно иметь возможность просматривать каждый элемент нового массива во вложенном цикле for, поэтому использование приведенного ниже метода не совсем приводит меня к моей конечной цели.

Excel vba - объедините несколько массивов в один

По сути, мне нужно объединить мой набор из 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

Любая помощь и / или руководство по этому поводу приветствуются!

MyArray1 = (1, 2, 3, 4) должен быть 4-мерным массивом или 1-мерным массивом с 4 элементами? (потому что ты тоже так не пишешь)

user4039065 18.07.2018 17:21

Какая конечная цель? Вы пытаетесь вставить в основной артрей только те значения, которые уже существуют или не существуют? Составьте более четкую картину того, чего вы пытаетесь достичь, может помочь получить лучшие результаты / ответы

Doug Coats 18.07.2018 17:49

@Jeeped, это все одномерные массивы.

Dean 18.07.2018 17:56

@DougCoats В основном хочу сравнить элементы каждого массива, mainArray и myArray. Если в myArray есть два или более элементов для каждого элемента в mainArray, либо удалите дубликаты, либо создайте новый массив только с элементами, которые появляются один раз.

Dean 18.07.2018 17:59

Ваш последний комментарий существенно меняет объем вашего вопроса, по сути, делая его совершенно новым вопросом. Добавленные вами условия должны быть включены в ваш вопрос через редактировать и даже добавлены в заголовок.

user4039065 18.07.2018 21:19
Структурированный массив Numpy
Структурированный массив Numpy
Однако в реальных проектах я чаще всего имею дело со списками, состоящими из нескольких типов данных. Как мы можем использовать массивы numpy, чтобы...
T - 1Bits: Генерация последовательного массива
T - 1Bits: Генерация последовательного массива
По мере того, как мы пишем все больше кода, мы привыкаем к определенным способам действий. То тут, то там мы находим код, который заставляет нас...
Что такое деструктуризация массива в JavaScript?
Что такое деструктуризация массива в JavaScript?
Деструктуризация позволяет распаковывать значения из массивов и добавлять их в отдельные переменные.
2
5
4 946
6
Перейти к ответу Данный вопрос помечен как решенный

Ответы 6

Если вы работаете с одномерными массивами, вы можете вместо этого использовать коллекцию. Намного лучше справляется с динамическим изменением размеров.

Вы можете объявить коллекцию, а затем добавить в нее каждый из элементов массивов. Тогда у вас будет один большой список со всеми значениями.

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

Вот введение в коллекцию: https://excelmacromastery.com/excel-vba-collections/

Используя ваши данные, вот как создать один массив из многих:

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

Поскольку OP имеет 46 массивов, с Redim Preserve вы создаете новый массив как минимум 46 раз, что, вероятно, может вызвать серьезное снижение производительности.

Zack 18.07.2018 20:41

@Zack - 46 массивов * 5 элементов на массив будет около 250 повторных инициализаций. Это не может снизить производительность в 2018 году. Если мы выполним повторную инициализацию примерно полмиллиона раз, то да, это будет немного медленнее, а выполнение однократной инициализации будет быстрее. Однако моя идея заключалась в том, чтобы показать, как происходит объединение массива.

Vityata 18.07.2018 21:11

Нет гарантии, что средний массив в фактическом коде OP содержит только 5 элементов.

Zack 18.07.2018 21:15

@Zack средний массив содержит около 15-20 элементов, умноженный на 46, то есть примерно 900 повторных инициализаций. Вряд ли вообще замедление.

Dean 19.07.2018 08:27

Альтернативный подход «по кирпичику».

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

Возможно это ...

'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

Проблема с использованием Redim Preserve для объединения массивов в том, что это может быть дорогооперация, поскольку вы в основном воссоздаете массив каждый раз, когда он вызывается. Поскольку у вас есть 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
Ответ принят как подходящий

Поскольку вы пишете в своих комментариях, что вашей конечной целью является создание массива уникальных элементов, вам может быть лучше всего использовать словарь, в котором вы можете проверить уникальность при добавлении каждого элемента в словарь. Что-то вроде:

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

Другие вопросы по теме