Скопируйте двумерный массив в другой массив на основе критериев в VBA. - PullRequest
0 голосов
/ 21 апреля 2020

Я хотел бы скопировать данные с одного листа на другой.

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

После заполнения двумерного массива (LookupSource) я хотел бы сохранить только некоторые записи, основанные на критериях (столбец A = 10000), поэтому я пытаюсь скопировать из LookupSource строки, извлекающие этот критерий, в двумерный массив (DataToCopy), который будет скопирован на лист назначения.

Моя проблема в том, что я не могу этого сделать, потому что, как мне кажется, я не могу сделать динамическое изменение размера c для первого измерения (строк) второго массива (DataToCopy).

Любая идея, как заполнить DataToCopy из LookupSource в зависимости от моего состояния?

Ошибка "index out of range", которую я получаю, находится в строке: ReDim Preserve DataToCopy(1 to j, 1 to 6)

не в в первый раз, но во второй раз, когда я ввожу For l oop после Next II, предположим, что это потому, что J является переменной, и мне не разрешено изменять первое измерение массив.

Как с этим справиться?

Есть ли какая-нибудь лучшая идея из того, что я делаю?

, чтобы дать вам пример, вот небольшая часть листа, который Я хочу скопировать (я взял только 8 строк, а в реале их тысячи). Я хочу скопировать только те строки, которые имеют 10000 в столбце A.

enter image description here

Вот мой код

Dim LookupSource as Variant      
Dim DataToCopy() As Variant        
Dim i As Long
Dim j As Long


With MySheet
'MyRange is a defined name that reprensent column A, B, C, D, E, F
LookupSource = .Range(.Range("MyRange")(1, 1), .Range("MyRange")(8, 6)).Value2

j = 1

For i = LBound(LookupSource) To UBound(LookupSource)

If LookupSource(i, 1) = 10073 Then
ReDim Preserve DataToCopy(1 to j, 1 to 6)
DataToCopy(j, 1) = LookupSource(i, 1)
DataToCopy(j, 2) = LookupSource(i, 2)
DataToCopy(j, 3) = LookupSource(i, 3)
DataToCopy(j, 4) = LookupSource(i, 4)
DataToCopy(j, 5) = LookupSource(i, 5)
DataToCopy(j, 6) = LookupSource(i, 6)
j = j + 1
End If

Next i

end with

Ответы [ 2 ]

0 голосов
/ 21 апреля 2020

Как преодолеть ограничения ReDim Preserve в многомерных массивах

Как уже упоминалось @ScottCraner, ReDim Preserve может изменить только последнее измерение данного массива (поля данных) , Поэтому попытка изменить размер первого измерения 2-мерного массива (= "строки") завершится неудачей.

Однако вы можете преодолеть это неудобство, применив относительно неизвестную возможность фильтрации Application.Index() (c .f. секция [2]) и прибыль от дополнительного бонуса за меньшее количество петель.

Дополнительная информация: см. Некоторые особенности Application.Index() функция

Sub GetRowsEqual10000()
    With Sheet1
        Dim lastRow As Long:  lastRow = .Range("A" & .Rows.Count).End(xlUp).Row
        Dim rng     As Range: Set rng = .Range("A2:F" & lastRow)
    End With

    '[1] get data
    Dim data: data = rng

    '[2] rearrange data via Application.Index() instead ReDim Preserve plus loops
    data = Application.Index(data, ValidRows(data, Condition:=10000), Array(1, 2, 3, 4, 5, 6))
End Sub

функция справки ValidRows ()

Function ValidRows(arr, Condition) As Variant
'Purpose: a) check condition (e.g. values equalling 10000) and b) get valid row numbers c) in a 2-dim vertical array
ReDim tmp(1 To UBound(arr))     ' provide for 1-based 2-dim array
Dim i As Long, ii As Long
For i = 1 To UBound(arr)                ' loop through 1st "column"
    If arr(i, 1) = Condition Then       '   a) check condition 
        ii = ii + 1: tmp(ii) = i        '   b) collect valid row numbers
    End If
Next i
ReDim Preserve tmp(1 To ii)             '   resize tmp array (here the 1st dimension is also the last one:) 
ValidRows = Application.Transpose(tmp)  ' c) return transposed result as 2-dim array
End Function

Редактировать в соответствии с комментарием (2020- 04-22)

Краткие указания на наиболее частое использование Application.Index():

Часто функция Application.Index() используется для получения целой строки или массив столбцов из 2-мерного массива без необходимости l oop.
Для доступа к массиву двухмерных полей данных на основе 1, например, требуется указать один номер строки или столбца и установите для столбца соседнего аргумента или номера строки значение 0 (ноль), соответственно, что может привести, например, к

        Dim horizontal, vertical, RowNumber As Long, ColumnNumber As Long
    RowNumber = 17: ColumnNumber = 4
    horizontal = Application.Index(data, RowNumber, 0)
    vertical   = Application.Index(data, 0, ColumnNumber)
* 1 048 * (Адресация к одному элементу array будет осуществляться напрямую, однако через data(i,j) вместо теоретического Application.Index(data, i, j))

Как использовать Application.Index() для реструктуризации / цели фильтрации :

Чтобы извлечь выгоду из расширенных возможностей Application.Index(), вам нужно передать не только имя массива (например, data), но и аргументы строки | столбца в виде массивов, например:

    data = Application.Index(data, Application.Transpose(Array(15,8,10)), Array(1, 2, 3, 4, 5, 6))

Обратите внимание, что параметр строк становится "вертикальным" 2-мерным массивом путем транспонирования, где Array(15,8,10) даже изменит существующий порядок строк ( в приведенном выше примере кода это делается в последней строке кода в функции ValidRows()). Аргумент столбцов Array(1,2,3,4,5,6), с другой стороны, остается "плоским" или "горизонтальным" и позволяет получить все существующие значения столбцов такими, как они есть.

Таким образом, вы в конечном итоге получаете любые данные элементы в заданных элементных индексах (представьте их как координаты в графике c).

0 голосов
/ 21 апреля 2020

Функция поиска диапазона

Код

Option Explicit

'START ****************************************************************** START'
' Purpose:      Filters a range by a value in a column and returns the result  '
'               in an array ready to be copied to a worksheet.                 '
'******************************************************************************'
Function RangeLookup(LookUpValue As Variant, LookupRange As Range, _
  Optional LookupColumn As Long = 1) As Variant

    Dim LookUpArray As Variant    ' LookUp Array
    Dim DataToCopy As Variant     ' DataToCopy (RangeLookup) Array
    Dim countMatch As Long        ' DataToCopy (RangeLookUp) Rows Counter
    Dim r As Long, c As Long      ' Row and Column Counters

    ' Check the arguments.
    Select Case VarType(LookUpValue)
         Case 2 To 8, 11, 17
         Case Else: Exit Function
    End Select
    If LookupRange Is Nothing Then Exit Function
    If LookupColumn < 1 Or LookupColumn > LookupRange.Columns.Count _
      Then Exit Function

    ' Copy values of Lookup Range to Lookup Array.
    LookUpArray = LookupRange

    ' Task: Count the number of values containing LookUp Value
    '       in LookUp Column of LookUp Array which will be
    '       the number of rows in DataToCopy Array.
    '       The number of columns in both arrays will be the same.

    ' Either:
    ' Count the number of values containing LookUp Value.
    countMatch = Application.WorksheetFunction _
      .CountIf(LookupRange.Columns(LookupColumn), LookUpValue)

    ' Although the previous looks more efficient, it should be tested.

'    ' Or:
'    ' Loop through rows of LookUpArray.
'    For r = 1 To UBound(LookUpArray)
'        ' Check if the value in current row in LookUp Column
'        ' is equal to LookUp Value.
'        If LookUpArray(r, LookupColumn) = LookUpValue Then
'            ' Increase DataCopy Rows Counter.
'            countMatch = countMatch + 1
'        End If
'    Next r

    ' Check if no match was found.
    If countMatch = 0 Then Exit Function

    ' Task: Write the matching rows in LookUp Array to DataToCopy Array.

    ' Resize DataToCopy Array to DataToCopy Rows counted in the previous
    ' For Next loop and the number of columns in Lookup Array.
    ReDim DataToCopy(1 To countMatch, 1 To UBound(LookUpArray, 2))
    ' Reset DataToCopy Rows Counter.
    countMatch = 0
    ' Loop through rows of LookUp Array.
    For r = 1 To UBound(LookUpArray)
        ' Check if the value in current row in LookUp Column
        ' is equal to LookUp Value.
        If LookUpArray(r, LookupColumn) = LookUpValue Then
            ' Increase DataCopy Rows Counter.
            countMatch = countMatch + 1
            ' Loop through columns of LookUp (DataToCopy) Array.
            For c = 1 To UBound(LookUpArray, 2)
                ' Write the current value of LookUp Array to DataToCopy Array.
                DataToCopy(countMatch, c) = LookUpArray(r, c)
            Next c
        End If
    Next r

    ' Write values from DataToCopy Array to RangeLookup Array.
    RangeLookup = DataToCopy

End Function
'END ********************************************************************** END'

Вы должны использовать его, например, так:

Sub TryRangeLookup()

    Dim LookupRange As Range
    Dim DataToCopy As Variant

    With MySheet
    'MyRange is a defined name that reprensent column A, B, C, D, E, F
        Set LookupRange = .Range(.Range("MyRange")(1, 1), _
          .Range("MyRange")(8, 6)).Value2
    End With
    RangeLookUp 10073, DataCopy   
    If Not IsArray(DataToCopy) Then 
        MsgBox "No data found.": Exit Sub ' or whatever...
    Endif
    ' Continue with code...

End Sub
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...