2015-10-06 6 views
2

Я использую VBA в Excel, чтобы использовать XML-файл и выгружать определенную информацию на отдельные вкладки. Я хочу объединить 2-мерные массивы. Массивы имеют «известное» количество столбцов, но «неизвестное» количество строк. Рассмотрим следующие два массива:Объединение 2D (2-мерных) массивов

array1:

a b c 
d e f 

array2:

1 2 3 
4 5 6 

Как объединить эти массивам, если я хочу следующий результат:

array3:

a b c 
d e f 
1 2 3 
4 5 6 

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

array4:

a b c 1 2 3 
d e f 4 5 6 

Я не могу найти ответ к этому где угодно.

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

Цель состоит в том, чтобы собрать большой массив, а затем записать его в лист Excel всего за один шаг, потому что, когда я делаю это по частям, производительность очень плохая.

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

Причина, по которой я спрашиваю об обоих способах, заключается в том, что на самом деле я хочу добавить последовательность последовательно. Например, предположим, у меня есть четыре массива, A, B, C, D.

Сначала добавьте массив A:

A 

Затем добавьте массив B:

A B 

Затем добавьте массив С:

A B 
C 

Затем добавьте массив D:

A B 
C D 

и так далее ...

Имейте в виду, что каждый из указанных массивов будет иметь такой размер, что они «подходят» правильно означая А и Б имеют одинаковое количество строк, но разное количество столбцов. С другой стороны, A и C имеют одинаковое количество столбцов, но различное количество строк. И так далее ...

Я хотел добавить демонстрацию, используя код Макро Человека снизу. Вот то, что он дал (я добавил немного, чтобы читатели могли просто скопировать/вставить):

Option Explicit 

Sub Testing() 

    Dim Array1(0 To 1, 0 To 2) As String 
    Array1(0, 0) = "a" 
    Array1(0, 1) = "b" 
    Array1(0, 2) = "c" 
    Array1(1, 0) = "d" 
    Array1(1, 1) = "e" 
    Array1(1, 2) = "f" 

    Dim Array2(0 To 1, 0 To 2) As String 
    Array2(0, 0) = "1" 
    Array2(0, 1) = "2" 
    Array2(0, 2) = "3" 
    Array2(1, 0) = "4" 
    Array2(1, 1) = "5" 
    Array2(1, 2) = "6" 

    Dim i As Long 
    For i = 1 To 25000 

     With Range("A" & Rows.Count).End(xlUp).Offset(IIf(IsEmpty([A1]), 0, 1), 0) 
      .Resize(UBound(Array1, 1) - LBound(Array1, 1) + 1, _ 
        UBound(Array1, 2) - LBound(Array1, 2) + 1).Value = Array1 
     End With 

     With Range("A" & Rows.Count).End(xlUp).Offset(IIf(IsEmpty([A1]), 0, 1), 0) 
      .Resize(UBound(Array2, 1) - LBound(Array2, 1) + 1, _ 
        UBound(Array2, 2) - LBound(Array2, 2) + 1).Value = Array2 
     End With 

    Next i 

End Sub 

При запуске приведенного выше кода, который идет обратно в таблицу каждый раз, чтобы написать небольшое количество данных, это занимает много времени.На моей двойной машине Xeon, например, 25-30 секунд.

Однако, если вы переписываете и заполняете массив FIRST, а затем пишите в электронную таблицу ONCE, он запускается примерно через одну секунду.

Option Explicit 

Sub Testing() 

    Dim Array1(0 To 99999, 0 To 2) As String 
    Array1(0, 0) = "a" 
    Array1(0, 1) = "b" 
    Array1(0, 2) = "c" 
    Array1(1, 0) = "d" 
    Array1(1, 1) = "e" 
    Array1(1, 2) = "f" 

    Dim i As Long 
    For i = 0 To 99999 

     Array1(i, 0) = "a" 
     Array1(i, 1) = "b" 
     Array1(i, 2) = "c" 

    Next i 

    With Range("A" & Rows.Count).End(xlUp).Offset(IIf(IsEmpty([A1]), 0, 1), 0) 
     .Resize(UBound(Array1, 1) - LBound(Array1, 1) + 1, _ 
       UBound(Array1, 2) - LBound(Array1, 2) + 1).Value = Array1 
    End With 

End Sub 

Я хотел бы увидеть решение, которое делает то же самое, за исключением возможности добавить «куски» данных вместо отдельных элементов. Добавление массивов в большие массивы, в идеале. Еще лучше было бы, если бы «родительский» массив каким-то образом динамически менялся.

Ответ Джона Колемана ниже работал отлично.

Я на самом деле в сочетании немного Macro Человека с испытанием() подпрограммой Джона и это динамически повторно размеры диапазон:

Option Explicit 

Sub test() 
    Dim A As Variant, B As Variant 
    ReDim A(0 To 1, 0 To 1) 
    ReDim B(0 To 1, 0 To 1) 
    A(0, 0) = 1 
    A(0, 1) = 2 
    A(1, 0) = 3 
    A(1, 1) = 4 
    B(0, 0) = 5 
    B(0, 1) = 6 
    B(1, 0) = 7 
    B(1, 1) = 8 

    Dim Array1 As Variant 
    Array1 = Combine(A, B) 

    With Range("A" & Rows.Count).End(xlUp).Offset(IIf(IsEmpty([A1]), 0, 1), 0) 
    .Resize(UBound(Array1, 1) - LBound(Array1, 1) + 1, _ 
      UBound(Array1, 2) - LBound(Array1, 2) + 1).Value = Array1 
    End With 
End Sub 
+0

Я думаю, что я недопонимаю «массив». Вы можете сохранить все эти значения в массив, а затем отобразить их в Excel, но вы хотите ... вы можете пройти три столбца, затем вниз по строке, повторить. Или вы можете пройти шесть столбцов и вниз. Я хочу сказать, что массив Excel (AFAIK) не хранится каким-то физическим способом ... это всего лишь список элементов. Как вы показываете их в Excel, вы до 100%. Имеет ли это смысл? – BruceWayne

+0

Я немного уточнил свой вопрос. это связано с VBA, который в конечном итоге записывается на лист excel. если я ударил лист, чтобы обновлять каждое значение или даже в больших кусках значений за раз, он берет навсегда. когда я перешел от хранения каждого значения отдельно, чтобы разместить их на листе в блоках, производительность значительно увеличилась. Я надеюсь найти способ сделать то же самое, но объединить массивы еще больше в VBA. – gotmike

+0

Команда «Redim PRESERVE» может только расширить последний ранг *. Вероятно, вам лучше создать новый массив, который затемняется с добавлением UBounds и заполняется тогда оттуда. Держитесь подальше от '.Transpose', чтобы выполнить команду redim. Ваши 100K «ряды» данных превышают его ограничения. – Jeeped

ответ

5

Вот функция VBA, которая может объединить два 2-мерных массивов в один двумерный массив. Он может использоваться либо из VBA, либо как формула массива непосредственно в Excel. Итерация неизбежна здесь в VBA, так как язык не имеет примитивы для таких вещей, как конкатенации массивы:

Function Combine(A As Variant, B As Variant, Optional stacked As Boolean = True) As Variant 
    'assumes that A and B are 2-dimensional variant arrays 
    'if stacked is true then A is placed on top of B 
    'in this case the number of rows must be the same, 
    'otherwise they are placed side by side A|B 
    'in which case the number of columns are the same 
    'LBound can be anything but is assumed to be 
    'the same for A and B (in both dimensions) 
    'False is returned if a clash 

    Dim lb As Long, m_A As Long, n_A As Long 
    Dim m_B As Long, n_B As Long 
    Dim m As Long, n As Long 
    Dim i As Long, j As Long, k As Long 
    Dim C As Variant 

    If TypeName(A) = "Range" Then A = A.Value 
    If TypeName(B) = "Range" Then B = B.Value 

    lb = LBound(A, 1) 
    m_A = UBound(A, 1) 
    n_A = UBound(A, 2) 
    m_B = UBound(B, 1) 
    n_B = UBound(B, 2) 

    If stacked Then 
     m = m_A + m_B + 1 - lb 
     n = n_A 
     If n_B <> n Then 
      Combine = False 
      Exit Function 
     End If 
    Else 
     m = m_A 
     If m_B <> m Then 
      Combine = False 
      Exit Function 
     End If 
     n = n_A + n_B + 1 - lb 
    End If 
    ReDim C(lb To m, lb To n) 
    For i = lb To m 
     For j = lb To n 
      If stacked Then 
       If i <= m_A Then 
        C(i, j) = A(i, j) 
       Else 
        C(i, j) = B(lb + i - m_A - 1, j) 
       End If 
      Else 
       If j <= n_A Then 
        C(i, j) = A(i, j) 
       Else 
        C(i, j) = B(i, lb + j - n_A - 1) 
       End If 
      End If 
     Next j 
    Next i 
    Combine = C 
End Function 

Я тестировал его 4 различными способами. Сначала я вошел ваши два примера массивов в таблицах и используется Combine непосредственно в Excel как формулу массива:

enter image description here

Здесь A7: С10 содержит формулу массива

{=combine(A1:C2,A4:C5)} 

и A12: F13 содержит формула массива

{=combine(A1:C2,A4:C5,FALSE)} 

Тогда я побежал следующие подразделы:

Sub test() 
    Dim A As Variant, B As Variant 
    ReDim A(0 To 1, 0 To 1) 
    ReDim B(0 To 1, 0 To 1) 
    A(0, 0) = 1 
    A(0, 1) = 2 
    A(1, 0) = 3 
    A(1, 1) = 4 
    B(0, 0) = 5 
    B(0, 1) = 6 
    B(1, 0) = 7 
    B(1, 1) = 8  
    Range("A15:B18").Value = Combine(A, B) 
    Range("C15:F16").Value = Combine(A, B, False)  
End Sub 

Выход:

enter image description here

+0

, который отлично подходит для этого примера. отличный ответ. – gotmike

1

Вы можете попробовать изменения размера назначения в соответствии размеров массива. Что-то вдоль линий:

(предполагается, что ваши массивы называются «Array1» и «array2») ...

With Range("A" & Rows.Count).End(xlUp).Offset(IIf(IsEmpty([A1]), 0, 1), 0) 
    .Resize(UBound(Array1, 1) - LBound(Array1, 1) + 1, _ 
      UBound(Array1, 2) - LBound(Array1, 2) + 1).Value = Array1 
End With 

With Range("A" & Rows.Count).End(xlUp).Offset(IIf(IsEmpty([A1]), 0, 1), 0) 
    .Resize(UBound(Array2, 1) - LBound(Array2, 1) + 1, _ 
      UBound(Array2, 2) - LBound(Array2, 2) + 1).Value = Array2 
End With 
+0

это работает, помещая данные на листе в соответствующие места. я уже делаю что-то подобное, и для размещения всех данных требуется много времени. я хотел бы сделать именно это в коде VBA (т. е. - память), а затем сбрасывать все это на лист одним выстрелом. Я добавлю еще одно разъяснение, чтобы продемонстрировать. – gotmike

3

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

Попробуйте это:

Function Combine(m, n) 
    Dim m1&, m2&, n1&, n2& 
    m1 = UBound(m, 1): m2 = UBound(m, 2) 
    n1 = UBound(n, 1): n2 = UBound(n, 2) 
    With Worksheets.Add 
     .[a1].Resize(m1, m2) = m 
     .[a1].Resize(n1, n2).Offset(m1) = n 
     Combine = .[a1].Resize(m1 + n1, m2) 
     Application.DisplayAlerts = False 
     Application.ScreenUpdating = False 
     .Delete 
     Application.DisplayAlerts = True 
     Application.ScreenUpdating = True 
    End With 
End Function 

Примечание: это только демо, чтобы показать доказательство концепции. В настоящее время он выполняет вертикальную укладку двух массивов 2d. Простота модификации, а также горизонтальная укладка.

Примечание: Я обычно против такого рода вещей, но если вы думаете об этом, лист Excel аналогичен действительно большому 2d-массиву, и хотя это действительно подход с использованием slumbmer, он быстрый и есть нет итераций!

+1

Интересный подход. Я сделал такие вещи (добавьте, а затем удалите лист для обработки массива) для сортировки массива. Было бы интересно провести некоторые временные тесты, чтобы увидеть, как скорость сравнивается с подходом VBA. –

+0

хорошо пункт. я предполагаю, что отключение обновления экрана (и любого вычисления) действительно ускорит работу. вид уникального решения также помогает предотвратить (или обходным) любые проблемы, связанные с несоответствия типов с различными типами данных в массивах. – gotmike

+0

Компиляция кода дает ошибку, поскольку переменная 'b' не объявлена. '. [a1] .Resize (n1, n2). Неверное значение (M1) = b'. Пожалуйста, замените 'b' в конце строки 7 на' n' –

Смежные вопросы