Отфильтровать 2D-массив и разрезать на 1D

Имейте импортированный массив с 3 столбцами:

Цель состоит в том, чтобы отфильтровать по 3-му столбцу, заданному значением (например, «1»), и вернуть 1D-массив в формулу UDF-Excel. Некоторые проблемы с моим кодом:

1.1) Есть ли лучший способ сократить код? Сделайте много подобных петель.

1.2) Поскольку в листе Excel много формул, код будет выполняться много раз. Могу ли я избежать, по крайней мере, чтобы UBounds считал длину исходного 2D-массива (поскольку они всегда будут оставаться одинаковыми)?

  1. Код не достигает «fArray = arr1D». Так что Он не будет ничего писать обратно. Кто-нибудь может помочь? Спасибо.

Часть моего кода после импорта значений из Excel-Range в 2D-массив:

Function fArray(ArrName As String, Optional Gruppe As Byte)

Dim arr As Variant, arr1D as Variant
Dim i as long, j as Long, c as long

    'Count Matches
    For i = LBound(arr, 1) To UBound(arr, 1)
        If arr(i, 3) = Gruppe Then           'Filter
            c = c + 1
        End If
    Next i
    
    'If no Match exit
    If c = 0 Then
        Exit Function
    End If
    
    ReDim arr1D(1 To c, 1 To 1)         

    'Generate new filtered Array    
    For i = 1 To UBound(arr, 1)
        If arr(i, 1) > 1 Then
            j = j + 1
            arr1D(j, 1) = arr(i, 1)         'Assign and Slice from 2D to 1D
            
        End If
    Next i
    
    fArray = arr1D                          'Write back to Excel-Formula (UDF)
    
End Function

Внутри вашего второго цикла есть As - это вызов метода?

Tim Williams 14.12.2020 00:31

Извините, исправил, не должно быть там

Eli3 14.12.2020 00:37

Я сомневаюсь, что вызов UBound() сильно повлияет на производительность, но вы можете сократить его до одного вызова, если сначала сохраните его в переменной. Счетчики циклов оцениваются только один раз - при первом входе в цикл - поэтому там нет повторного попадания. К вашему сведению, ваш возвращаемый массив является двумерным, таким же, как и входной массив: единственная разница заключается в верхнем пределе второго измерения.

Tim Williams 14.12.2020 01:33

Ваш код не возвращает ожидаемый результат, потому что вы проверяете первый столбец, если он больше 1 If arr(i, 1) > 1 Then, а не третий If arr(i, 3) > 1 Then. Также неясно, что вы хотите вернуть: значения из первого или третьего столбца, т.е. arr1D(j, 1) = arr(i, 1) или arr1D(j, 1) = arr(i, 3) в 2D или 1D-массиве, то есть Redim arr1D(1 To c, 1 To 1) или Redim arr1D(1 To c).

VBasic2008 14.12.2020 06:31

@ VBasic2008 Хотите вернуть только значения из первого столбца. Если я перейду на If arr(i, 3) > 1 и/или Redim arr1D(1 To c), то индекс в arr1D выйдет за пределы допустимого диапазона. Это приведет к тому, что arr1D не будет заполнен. В этом отношении мой код был правильным, а в остальном нет.

Eli3 14.12.2020 17:38

Это сработает, если вы используете arr1D(j) = arr(i, 1). Одно измерение (1D) означает, что у вас нет , 1.

VBasic2008 14.12.2020 17:44
Структурированный массив Numpy
Структурированный массив Numpy
Однако в реальных проектах я чаще всего имею дело со списками, состоящими из нескольких типов данных. Как мы можем использовать массивы numpy, чтобы...
T - 1Bits: Генерация последовательного массива
T - 1Bits: Генерация последовательного массива
По мере того, как мы пишем все больше кода, мы привыкаем к определенным способам действий. То тут, то там мы находим код, который заставляет нас...
Что такое деструктуризация массива в JavaScript?
Что такое деструктуризация массива в JavaScript?
Деструктуризация позволяет распаковывать значения из массивов и добавлять их в отдельные переменные.
1
6
241
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Массив, Словарь, ArrayList

  • TESTfArray проверяет вашу функцию fArray.
  • fArray показывает, как вы могли бы использовать три следующие функции в вашем случае.
  • vLookupArray, vLookupDictionary и vLookupArrayList показывают три решения вашего случая. Они в основном делают одно и то же. Все решения вернут одномерный массив. vLookupArray вернет массив с отсчетом от единицы, а два других решения вернут массив с отсчетом от нуля. Что эффективнее, решать вам.
  • TESTvLookup просто проверит все три функции.

Код

Option Explicit

Sub TESTfArray()
    Dim arr As Variant
    arr = fArray("NamedRangeName", 1)
    If Not IsEmpty(arr) Then
        Debug.Print Join(arr, vbLf)
    End If
End Sub

Function fArray(ArrName As String, Optional Gruppe As Byte) As Variant
    
    Dim arr As Variant
    
    ' Code to get 'arr' from ArrName.
    ' e.g.:
    arr = Range(ArrName).Value
    'arr = Range("A1").CurrentRegion
    
    If Not IsEmpty(arr) Then
        fArray = vLookupArray(arr, Gruppe, 3, 1)
        'fArray = vLookupDictionary(arr, Gruppe, 3, 1)
        'fArray = vLookupArrayList(arr, Gruppe, 3, 1)
    End If

End Function

Function vLookupArray(TwoD As Variant, _
    ByVal LookupValue As Variant, _
    ByVal LookupColumn As Long, _
    ByVal ReturnColumn As Long) _
As Variant
    Dim rCount As Long: rCount = UBound(TwoD, 1)
    Dim OneD As Variant: ReDim OneD(1 To rCount)
    Dim i As Long
    Dim n As Long
    For i = 1 To rCount
        If TwoD(i, LookupColumn) = LookupValue Then
            n = n + 1
            OneD(n) = TwoD(i, ReturnColumn)
        End If
    Next i
    If n > 0 Then
        ReDim Preserve OneD(1 To n)
        vLookupArray = OneD
        'Debug.Print "Array:" & vbLf & Join(vLookupArray, vbLf)
    End If
End Function

' Dictionary: Item, Count, Keys
Function vLookupDictionary(TwoD As Variant, _
    ByVal LookupValue As Variant, _
    ByVal LookupColumn As Long, _
    ByVal ReturnColumn As Long) _
As Variant
    With CreateObject("Scripting.Dictionary")
        Dim i As Long
        For i = 1 To UBound(TwoD, 1)
            If TwoD(i, LookupColumn) = LookupValue Then
                .Item(TwoD(i, ReturnColumn)) = Empty
            End If
        Next i
        If .Count > 0 Then
            vLookupDictionary = .Keys
           'Debug.Print "Dictionary:" & vbLf & Join(vLookupDictionary, vbLf)
        End If
    End With
End Function

' ArrayList: Add, Count, ToArray
Function vLookupArrayList(TwoD As Variant, _
    ByVal LookupValue As Variant, _
    ByVal LookupColumn As Long, _
    ByVal ReturnColumn As Long) _
As Variant
    With CreateObject("System.Collections.ArrayList")
        Dim i As Long
        For i = 1 To UBound(TwoD, 1)
            If TwoD(i, LookupColumn) = LookupValue Then
                .Add TwoD(i, ReturnColumn)
            End If
        Next i
        If .Count > 0 Then
            vLookupArrayList = .ToArray
            'Debug.Print "ArrayList:" & vbLf & Join(vLookupArrayList, vbLf)
        End If
    End With
End Function

Sub TESTvLookup()
    Dim cel As Range
    Set cel = Range("A2")
    Dim rng As Range
    With cel.CurrentRegion
        Set rng = cel.Resize( _
            .Rows.Count + .Row - cel.Row, _
            .Columns.Count + .Column - cel.Column)
    End With
    Debug.Print "Range Address:" & vbLf & rng.Address(0, 0)
    Dim TwoD As Variant
    TwoD = rng.Value
    Dim OneD As Variant
    OneD = vLookupArray(TwoD, 1, 3, 1)
    If Not IsEmpty(OneD) Then
        Debug.Print "Array:" & vbLf & Join(OneD, vbLf)
    End If
    OneD = vLookupDictionary(TwoD, 1, 3, 1)
    If Not IsEmpty(OneD) Then
        Debug.Print "Dictionary:" & vbLf & Join(OneD, vbLf)
    End If
    OneD = vLookupArrayList(TwoD, 1, 3, 1)
    If Not IsEmpty(OneD) Then
        Debug.Print "ArrayList:" & vbLf & Join(OneD, vbLf)
    End If
End Sub

Спасибо, это сработало отлично! Словарь был моим любимым. К сведению: этот код не может работать в функции: arr = Range("A1").CurrentRegion . Смотрите здесь: [ссылка]stackoverflow.com/questions/65179281/…

Eli3 14.12.2020 18:03

Спасибо за совет Current Region, я понятия не имел. Будьте осторожны со словарем, он вернет уникальные значения из столбца 1, которые уже уникальны в вашем случае. В других случаях вам это может не понадобиться, и вам придется использовать другие решения. В других случаях вам могут понадобиться уникальные значения, когда будет работать только словарь.

VBasic2008 14.12.2020 18:21

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