На самом деле весь необходимый код - всего несколько строк:
Sub test()
Dim arr As Variant: arr = Array("A", "A", "C", "D", "A", "E", "G")
With Application
uniques = .Index(arr, 1, Filter(.IfError(.Match(.Transpose(.Evaluate("ROW(1:" & UBound(.Match(arr, arr, 0)) & ")")), .Match(arr, arr, 0), 0), "|"), "|", False))
End With
End Sub
Вышеприведенный код вернет 1D-массив, возвращающий все уникальные элементы в нашем исходном массиве:
Объяснение:
Строка, которая извлекает все эти значения, выглядит интенсивно, Давайте разберем его на части:
Application.Match
имеет возможность работать с массивами в своих параметрах. Итак, в основном мы смотрим на: .Match({"A","A","C","D","A","E","G"},{"A","A","C","D","A","E","G"},0)
. Тогда возвращаемый массив будет иметь вид: {1,1,3,4,1,6,7}
, и на самом деле это первые позиции, в которых находится каждое значение. Этот результат будет основой для дальнейшего развития.
Мы можем видеть третий .Match
в нашем коде, который мы нужно в основном сказать следующее: .Match({1,2,3,4,5,6,7},{1,1,3,4,1,6,7},0)
. Первый параметр - это то, что получается с помощью выделенного выше кода.
Где .Evaluate("ROW(1:" & UBound(.Match(arr, arr, 0)) & ")")
вернет массив значений из 1-7
, Application.Transpose
вернет его так, что это 1D-массив.
На последнем шаге будет возвращен массив, содержащий ошибки, однако код не сломается, поскольку мы используем Application
вместо WorksheetFunction
. Полученный массив будет выглядеть как {1,Error 2042,3,4,Error 2042,6,7}
. Теперь весь смысл состоит в том, чтобы избавиться от Error
значений.
Способ сделать это - через Application.IfError
, который оценит массив и изменит все значения ошибок на значение данной строки. В нашем случае я использовал символ трубы. Пользователь должен выбрать символ, достаточно уникальный, чтобы он не отображался ни в одном из элементов исходного массива. Итак, после оценки. Наш текущий массив будет выглядеть так: {1,|,3,4,|,6,7}
.
Теперь мы получили массив с символами канала, которые мы бы хотели, чтобы они вышли! Быстрый способ сделать это с помощью функции Filter
. Filter
возвращает массив с элементами или без них, которые соответствуют нашим критериям (в зависимости от TRUE
или FALSE
в его третьем параметре).
Итак, в основном мы хотим вернуть массив следующим образом: Filter(<array>, "|", False)
. Результирующий 1D-массив теперь выглядит следующим образом: {1,3,4,6,7}
.
В некоторой степени он у нас уже есть. Нам просто нужно вырезать правильные значения из нашего исходного массива. Для этого мы можем использовать Application.Index
. Мы просто хотим указать .Index
, какие строки нам интересны. Для этого мы можем загрузить наш ранее найденный 1D-массив. Таким образом, код будет выглядеть так: .Index(arr1, <array>, 1)
, что приведет к созданию 1D-массива: {"A","C","D","E","G"}
Вывод:
Вот так. Одна строка (с более чем одной операцией) для извлечения 1D-массива уникальных значений из другого 1D-массива без итерации . Этот код готов к использованию в любом 1D-массиве, объявленном с arr
.
Полезно? Я не уверен на 100%, но я наконец достиг того, что я пытался в своем проекте. Полученный массив можно сразу использовать в любой задаче, в которой нужно использовать уникальные значения.
Сравнение: словарь и приложение. Методы:
При сравнении случайных предметов в Range(A1:A50000)
производительность действительно получает удар. Таким образом, сравнение времени между итеративным словарем и не итеративным подходом Application.Methods
выполняется с шагом 1000 пунктов. Ниже результата отметки 1000 предметов и каждых 10000 предметов (в секундах):
| Items | Dictionary | Methods |
|------- |------------ |------------- |
| 1000 | 0,02 | 0,03 |
| 10000 | 0 | 0,88 |
| 20000 | 0,02 | 3,31 |
| 30000 | 0,02 | 7,3 |
| 40000 | 0,02 | 12,84 |
| 50000 | 0,03 | 20,2 |
Используемый подход Dictionary
:
Sub Test()
Dim arr As Variant: arr = Application.Transpose(Range("A1:A50000"))
Dim dict As Object: Set dict = CreateObject("Scripting.Dictionary")
Dim x As Long
For x = LBound(arr) To UBound(arr)
dict(arr(x)) = 1
Next x
Dim uniques As Variant: uniques = dict.Keys
End Sub
Вывод: До 1000 элементов этот метод будет примерно равным по времени обработки по сравнению с более распространенной практикой Dictionary
. В любом большем, итерация (через память) всегда побеждает методический подход!
Я уверен, что время обработки будет более ограничено с новыми функциями массива Dynami c, когда показано @ ScottCraner.