Имейте импортированный массив с 3 столбцами:
Цель состоит в том, чтобы отфильтровать по 3-му столбцу, заданному значением (например, «1»), и вернуть 1D-массив в формулу UDF-Excel. Некоторые проблемы с моим кодом:
1.1) Есть ли лучший способ сократить код? Сделайте много подобных петель.
1.2) Поскольку в листе Excel много формул, код будет выполняться много раз. Могу ли я избежать, по крайней мере, чтобы UBounds считал длину исходного 2D-массива (поскольку они всегда будут оставаться одинаковыми)?
Часть моего кода после импорта значений из 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
Извините, исправил, не должно быть там
Я сомневаюсь, что вызов UBound() сильно повлияет на производительность, но вы можете сократить его до одного вызова, если сначала сохраните его в переменной. Счетчики циклов оцениваются только один раз - при первом входе в цикл - поэтому там нет повторного попадания. К вашему сведению, ваш возвращаемый массив является двумерным, таким же, как и входной массив: единственная разница заключается в верхнем пределе второго измерения.
Ваш код не возвращает ожидаемый результат, потому что вы проверяете первый столбец, если он больше 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 Хотите вернуть только значения из первого столбца. Если я перейду на If arr(i, 3) > 1
и/или Redim arr1D(1 To c)
, то индекс в arr1D выйдет за пределы допустимого диапазона. Это приведет к тому, что arr1D не будет заполнен. В этом отношении мой код был правильным, а в остальном нет.
Это сработает, если вы используете arr1D(j) = arr(i, 1)
. Одно измерение (1D) означает, что у вас нет , 1
.
TESTfArray
проверяет вашу функцию fArray
.fArray
показывает, как вы могли бы использовать три следующие функции в вашем случае.vLookupArray
, vLookupDictionary
и vLookupArrayList
показывают три решения вашего случая. Они в основном делают одно и то же. Все решения вернут одномерный массив. vLookupArray
вернет массив с отсчетом от единицы, а два других решения вернут массив с отсчетом от нуля. Что эффективнее, решать вам.TESTvLookup
просто проверит все три функции.Dictionary
объекте здесь , здесь и здесь , а о ArrayList
здесь и здесь.Код
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/…
Спасибо за совет Current Region
, я понятия не имел. Будьте осторожны со словарем, он вернет уникальные значения из столбца 1, которые уже уникальны в вашем случае. В других случаях вам это может не понадобиться, и вам придется использовать другие решения. В других случаях вам могут понадобиться уникальные значения, когда будет работать только словарь.
Внутри вашего второго цикла есть
As
- это вызов метода?