Альтернативный подход с использованием массивов плюс функции Excel Office 365
"Я предполагаю, что это может быть достигнуто с помощью циклов и нескольких операторов IF, просто не уверен должно ли это быть помещено в массив или словарь? "
Мой стимул , чтобы этот (поздний) ответ должен был продемонстрировать хитрую комбинацию методов массива и преобразования через Application.Index()
и Application.Match()
(исключая, между прочим, в основном If
с или циклы) с новыми динамическими c функциями Office 365 SORT()
и UNIQUE()
.
Функция UNIQUE возвращает список уникальных значений в списке или диапазоне. Применение Evaluate
к этим ` WorksheetFunctions позволяет присвоить найденные значения 2-мерному массиву, например,
myArray = Evaluate("=SORT(UNIQUE(D2:D17))")
Предупреждение:
Эта функция в настоящее время доступна подписчикам Office 365 на ежемесячном канале. Он будет доступен для подписчиков Office 365 на полугодовом канале, начинающемся в июле 2020 года.
Мое намерение - показать интересную альтернативу обычным циклам, но не конкурировать с решением выше по скорости или красота.
Пример вызова
Sub testUnique()
With Sheet1
'[1a] get lastRows (differ from values in D:E, see OP!)
Dim MasterListRows As Long, WeeklyListRows As Long
MasterListRows = .Cells(.Rows.Count, 1).End(xlUp).Row
WeeklyListRows = .Cells(.Rows.Count, 2).End(xlUp).Row
'[1b] get related ranges
Dim MasterListRange As Range, WeeklyListRange As Range
Set MasterListRange = .Range("D2:D" & MasterListRows)
Set WeeklyListRange = .Range("E2:E" & WeeklyListRows)
'~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
'[2] get complete set of all uniques in columns D:E
'~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
' Caveat: function uses Office365 UNIQUE() + SORT()
Dim allUniques
allUniques = getUniques(MasterListRange, WeeklyListRange)
'[3] write results to target
Dim tgt As Range
Set tgt = .Range("F2").Resize(UBound(allUniques), 1)
'write uniques to columns F:G
tgt.Resize(Columnsize:=2) = allUniques ' needs 2 columns
'(optional/cosmetic) - adapt upper case vs proper case
tgt.Offset(0, 0) = Evaluate("UPPER(" & tgt.Address & ")")
tgt.Offset(0, 1) = Evaluate("PROPER(" & tgt.Offset(0, 1).Address & ")")
End With
End Sub
Функции справки
Function getUniques(aRange As Range, bRange As Range)
Dim a As Long: a = aRange.Rows.Count
Dim b As Long: b = bRange.Rows.Count
'add bRange items to aRange
Dim addedRange As Range
Set addedRange = aRange.Offset(a).Resize(b, 1)
addedRange.Value = bRange.Value ' add bRange items temporarily to get all
'~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
'get all uniques as 1-based 2-dim "vertical" array ...
'~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Dim all: all = Evaluate("=SORT(UNIQUE(D2:D" & (a + b + 1) & "))")
'...and add 2nd column (needed in OP)
all = Application.Index(all, Evaluate("row(1:" & UBound(all) & ")"), Array(1, 1))
addedRange = vbNullString ' clear temporary items in addedRange
'~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
'identify master elements not contained in weeklyListRange
'~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
'(1-based 2-dim array with either row numbers of found elements or Error value 2042)
Dim nums: nums = Compare(aRange, bRange, bSort:=True) ' << see function Compare() below
'...remove not existing weekly list items in corresponding row (2nd column)
Dim i As Long
For i = 1 To UBound(nums)
If IsError(nums(i, 1)) Then all(i, 2) = "***" ' empty 2nd column
Next i
'return all as function result
getUniques = all
End Function
Function Compare(aRange As Range, bRange As Range, Optional bSort As Boolean = False)
'Note : called by the above help function
'Purpose: check the aRange array and return a 1-based 2-dim array containing
' a) row numbers of corresponding elements in bRange or
' b) Error value 2042 entries
'Hint : note that the 2nd MATCH argument is also a 1-dim array (differring from usual function calls)
Dim a, b
If bSort Then
a = Evaluate("=SORT(" & aRange.Address & ")")
b = Application.Transpose(Evaluate("=SORT(" & bRange.Address & ")"))
Else
a = aRange: b = Application.Transpose(bRange)
End If
'~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Compare = Application.Match(a, b, 0)
'~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
End Function