Using VBA to compare entire excel documents and output a new document, with rollup/totals of duplicates - excel

I currently have a script (hobbled together from SO), that compares two documents (50 columns x 1600 rows) and creates a new document containing the discrepancies.
Often the same discrepancy will be duplicated many (every) time due to it being an expected conversion.
Is there anyway to refine the output of the script?
Right now it just out puts the First column of worksheetA, plus the related discrepancy fields/cells.
I was sort of envisioning a roll up and total if there are duplicate of the same expected error to make the unexpected errors more visible.
Thank you so much in advance for any advice:
'Look for the [] and replace
'So [User] = yourusername
'And [Worksheetname] = yourworksheetname
' etc
Option Compare Text
Sub CompareWorkbooks()
Dim varSheetA As Variant
Dim varSheetB As Variant
Dim strRangeToCheck As String
Dim iRow As Long
Dim iCol As Long
nlin = 1
ncol = 1
'Get the worksheets from the workbooks
Set wbkA = Workbooks.Open(Filename:="C:\Users\[User]\[Path]\[Workbookname].xlsm") ' or whatever workbook path
Set varSheetA = wbkA.Worksheets("[WorksheetName]") ' or whatever sheet you need
Set wbkB = Workbooks.Open(Filename:="C:\Users\[User]\[Path]\[Workbookname].xlsm")
Set varSheetB = wbkB.Worksheets("Sheet1") ' or whatever sheet you need
Set wbkC = Workbooks.Open(Filename:="C:\Users\[User]\[Path]\[Workbookname].xlsm")
strRangeToCheck = "A2:BX2000" ' If you know the data will only be in a smaller range, reduce the size of the ranges above.
Debug.Print Now
varSheetA = wbkA.Worksheets("[WorksheetName]").Range(strRangeToCheck)
varSheetB = wbkB.Worksheets("[WorksheetName]").Range(strRangeToCheck) ' or whatever your other sheet is.
Debug.Print Now
For iRow = LBound(varSheetA, 1) To UBound(varSheetA, 1)
For iCol = LBound(varSheetA, 2) To UBound(varSheetA, 2)
If varSheetA(iRow, iCol) = varSheetB(iRow, iCol) Then
' Cells are identical.
' Do nothing.
Else
' Cells are different. Let's fill our main template with the information
Workbooks("New.xlsm").Activate
Cells(nlin, ncol) = varSheetA(iRow, 1) 'Gives the AID of the related changed field
Cells(nlin, ncol + 1) = varSheetA(iRow, iCol) 'Gives me the value in workbookA
Cells(nlin, ncol + 2) = varSheetB(iRow, iCol) 'Gives me the value in workbookB
nlin = nlin + 1
End If
Next
Next
End Sub

Related

Find the maximum consecutive repeated value on the bases of two columns

I need the expert help in VBA as I am new. Actually I am looking for Vba code for Consecutive Count on the bases of two column (Serial Number and Alert Code) on button click event. The Column row are not fixed (dynamically change). The Consecutive count is maximum repeat count for Alert Code per Serial number. This should display in output worksheet as per max repeat Alert count per Serial number
Input Worksheet:
Expected Output :
The repeat count work as below pattern from Input sheet (Just for reference only).
Mine source code as below but this does not reference the 1st Column Serial Number (This only work for One column like AlertCode) :
Sub ConsecutiveCount()
Dim lr As Long, c As Range, a As Long
Application.ScreenUpdating = False
lr = Worksheets("Count2").Cells(Rows.Count, 1).End(xlUp).Row
For Each c In Range("B2:B" & lr)
If c.Value <> c.Offset(1).Value Then
a = Cells(c.Row, 3).End(xlUp).Row
' Range(Cells(c.Row, 4), Cells(c.Row, 4).End(xlUp).Offset(1)).Value = c.Row - a
Cells(c.Row, 3).Value = c.Row - a
Else
End If
Next c
Application.ScreenUpdating = True
End Sub
Current Output (Serial number not included)
Screenshot(s) / here(♪) refers:
Named ranges/setup
First, define a couple of named ranges to assist with referencing / formulating in VBA:
Name: range_data: dynamic range that references the two columns of interest (here, col 1&2 in Sheet1):
Refers to: =Sheet1!$D$3:OFFSET(Sheet1!$E$3,COUNTA(Sheet1!$E$3:$E$99995)-1,0,1,1)
Name: range_summary_startcell: a static range that references the desired upper-left cell of the output table / summary.
Refers to: =Sheet1!$G$3
The summary table itself shall comprise a number of rows (depending upon range_data) and 3 columns (given the input/Q) - this will be produced by the macro (code below) and can be seen in screenshot above (G3:I5) - the macro functions shall determine the appropriate dimensions automatically
Code
With these two named ranges (i.e. 'range_data' & 'range_summary_startcell') defined, the following VB code produces the desired output per your Q:
Sub Macro_Summary()
'
'JB_007 07/01/2022
'
'
Application.ScreenUpdating = True
Range("range_summary_startcell").Select
ActiveCell.Formula2R1C1 = "=UNIQUE(range_data)"
ActiveSheet.Calculate
x = ActiveCell.End(xlDown).Row
Set range_count = ActiveCell.Offset(0, 2)
range_count.Select
range_count.Formula2R1C1 = _
"=COUNTIFS(INDEX(range_data,0,2),RC[-1],INDEX(range_data,0,1),RC[-2])"
Selection.AutoFill Destination:=Range(range_count, range_count.Offset(x - range_count.Row))
ActiveSheet.Calculate
End Sub
Caveats: assumes you have Office 365 compatible version of Excel
GIF - Running Macro
Notes (♪) saved as macro-free workbook for your own security if you wish to download underlying workbook - otherwise identical to screenshot(s) in this proposed soln.
Sub ConsecutiveCount()
Dim srcLastRow As Long, cntConsec As Long, i As Long
Dim rng As Range
Dim srcArr() As Variant
Dim srcSht As Worksheet
Dim destsht As Worksheet
Dim destArr() As Variant
Dim combID As String
Dim splitID As Variant
Application.ScreenUpdating = False
Set srcSht = Worksheets("Input")
Set destsht = Worksheets("Output")
With srcSht
srcLastRow = .Cells(.Rows.Count, "A").End(xlUp).Row + 1 ' include 1 blank line
srcArr = .Range(.Cells(2, "A"), .Cells(srcLastRow, "B"))
End With
Dim dict As Object
Dim dKey As Variant
Set dict = CreateObject("Scripting.dictionary")
cntConsec = 0
For i = LBound(srcArr) To UBound(srcArr)
cntConsec = cntConsec + 1
If i <> UBound(srcArr) Then
If srcArr(i, 1) <> srcArr(i + 1, 1) Or srcArr(i, 2) <> srcArr(i + 1, 2) Then
combID = srcArr(i, 1) & "|" & srcArr(i, 2)
If dict.Exists(combID) Then
' check if sum is more
If dict(combID) < cntConsec Then ' If new max for combination
dict(combID) = cntConsec
End If
Else
' add to dictionary
dict(combID) = cntConsec
End If
cntConsec = 0
End If
End If
Next i
ReDim destArr(1 To dict.Count, 1 To 3)
i = 0
For Each dKey In dict.keys
splitID = Split(dKey, "|")
i = i + 1
destArr(i, 1) = splitID(0)
destArr(i, 2) = splitID(1)
destArr(i, 3) = dict(dKey)
Next dKey
destsht.Range("A2").Resize(UBound(destArr), 3).Value = destArr
Application.ScreenUpdating = True
End Sub

Object variable or with block variable not set error for creating worksheets

The code below works fine on its own but once i add option explicit at the start, the object variable or with block variable not set error appears at the sheetname = index3. I have seen other threads where the issue is solved by set sheetname = ThisWorkbook.Worksheets("") but there are 3 sheets that will be created before i used this statement so i don't think it can work that way. Any ideas to solve this?
Option explicit
Private Sub createvramp()
Static count As Long
Dim iRow As Long
Dim aRow As Long
Dim a As Long
Dim b As Long
Dim selectRange As Range
Dim lastline As Integer
Dim sheetname As Worksheet
Dim indexrowcount As Integer
j = 2
iRow = 1
lastline = ActiveSheet.UsedRange.Rows.count
While iRow < lastline + 1
a = iRow + 1
b = iRow + 99 ' Max Group Size with Same name in F to H column
count = 1
If Cells(iRow, "H").Value = "Vramp_M1" Then
sheetname = "Index1"
ElseIf Cells(iRow, "H").Value = "Vramp_M2" Then
sheetname = "Index2"
Else
sheetname = "Index3" '<-------error occurs here
End If
For aRow = a To b
If Cells(iRow, "H") = Cells(aRow, "H") And Cells(iRow, "I") = Cells(aRow, "I") And Cells(iRow, "J") = Cells(aRow, "J") Then
count = count + 1
Else
Set selectRange = Range("A" & iRow & ":AP" & aRow - 1)
selectRange.Copy
indexrowcount = Sheets(sheetname).UsedRange.Rows.count + 1
Sheets(sheetname).Range("A" & indexrowcount).PasteSpecial xlPasteAll
iRow = iRow + count
Exit For
End If
Next aRow
Wend
If you just want to set the sheet name in your code, use Dim sheetname As String. Option Explicit helps a lot, but you must be very careful when you declare variables...
It happened that the error to appear on that row only because the first two conditions were False...
It is also good to avoid using of ActiveSheet, Sheets(...), Range("A" ...), Cells(aRow,...).
Everything expressed in this way refers to ActiveSheet. When you will work on a different sheet, maybe on a different workbook and you need to process a specific sheet (Sheets(sheetname).Range...), you may face a big mess. Try to define the sheet with reference to its workbook. Each range to reference the sheet where it belongs...
At least Dim Sh as Worksheet followed by Set Sh = ActiveSheet (use your working sheet). And then use Sh.Range("A...), Sh.Cells(aRow,...) and so on...
It is recommended to cultivate good habits which will help you in the future...

How to find duplicates and list them separately using VBA in Excel 2016

The answer "How to find duplicates and list them separately using VBA in Excel?" is exactly what I am looking for. I need help editing the language to fit my spreadsheet. Any help is greatly appreciated. I am not a programmer, just an enduser who admires the power of VBA. Thank you.
I've tried to edit the syntax but i cannot get it work in my spreadsheet.
Sub find_dups()
' Create and set variable for referencing workbook
Dim wb As Workbook
Set wb = ThisWorkbook
' Create and set variable for referencing worksheet
Dim ws As Worksheet
Set ws = wb.Worksheets("Sheet1")
' Find current last rows
' For this example, the data is in column A and the duplicates are in column C
Dim lngLastRowME As Long
lngLastRowME = ws.Range("A29-7834-9-0003").End(xlUp).Row
Dim lngLastRowDups As Long
lngLastRowDups = ws.Range("C29-7834-9-0003").End(xlUp).Row
' Create and set a variable for referencing data range
Dim rngME As Range
Set rngME = ws.Range("a4:a" & lngLastRowME)
Dim lngRowCount As Long
lngRowCount = 0
Dim clME As Variant
Dim lngCount As Long
Dim lngRowIndexME As Long
Dim lngRowIndexDups As Long
lngRowIndexDups = lngLastRowDups + 1
' Variable to store those values we've already checked
Dim strAlreadySearched As String
For Each clME In rngME.Cells
' Reset variables
lngCount = 0
' See if we've already searched this value
If InStr(1, strAlreadySearched, "|" & clME.Value & "|") = 0 Then
' We haven't, so proceed to compare to each row
For lngRowIndexME = 1 To lngLastRowME
' If we have a match, count it
If rngME.Cells(lngRowIndexME, 1).Value = clME.Value Then
lngCount = lngCount + 1
End If
Next lngRowIndexME
' If more than 1 instance
If lngCount > 1 Then
' Dup's were found, fill in values under duplicates
ws.Cells(lngRowIndexDups, 3).Value = clME.Value
ws.Cells(lngRowIndexDups, 4).Value = lngCount
' Drop down a row
lngRowIndexDups = lngRowIndexDups + 1
' Capture this value so we don't search it again
strAlreadySearched = strAlreadySearched & "|" & clME.Value & "|"
End If
End If
Next clME
End Sub
runtime error 400
Screen Shot and Workbook

Excel - how to split a list in a cell into separate cells

I am trying to split these names up so that are in a cell each. I have tried text to columns and that doesn't do the job.
you can use the code below to split the string by chr(10), which presents a line feed character. Please note the assumption behind input data header and output column. Please also note that dictionary works in this case only because dictionaries in VBA somehow maintains ordering - this is unlikely to be the case in other programming languages.
Public Sub sub_test()
Dim wsThis As Worksheet: Set wsThis = ActiveSheet
Dim vData As Variant
Dim dicOutput As Object: Set dicOutput = CreateObject("scripting.dictionary")
Dim vTemp As Variant
Dim vLine As Variant
Dim i As Long
With wsThis
' Read data into memory - assume no header
vData = .Range(.Range("A1"), .Range("A1").End(xlDown))
' Loop through each row
For i = LBound(vData, 1) To UBound(vData, 1)
' Split by new line
vTemp = Split(vData(i, 1), Chr(10))
For Each vLine In vTemp
' Check if new line is empty string
If Trim(vLine) <> vbNullString Then
dicOutput(vLine) = 1
End If
Next vLine
Next i
vTemp = Application.Transpose(dicOutput.keys)
' Output into worksheet - assume column C
.Range("C1").Resize(UBound(vTemp, 1) - LBound(vTemp, 1) + 1, UBound(vTemp, 2) - LBound(vTemp, 2) + 1) = vTemp
End With
End Sub

Populate unique values into a VBA array from Excel

Can anyone give me VBA code that will take a range (row or column) from an Excel sheet and populate a list/array with the unique values,
i.e.:
table
table
chair
table
stool
stool
stool
chair
when the macro runs would create an array some thing like:
fur[0]=table
fur[1]=chair
fur[2]=stool
Sub GetUniqueAndCount()
Dim d As Object, c As Range, k, tmp As String
Set d = CreateObject("scripting.dictionary")
For Each c In Selection
tmp = Trim(c.Value)
If Len(tmp) > 0 Then d(tmp) = d(tmp) + 1
Next c
For Each k In d.keys
Debug.Print k, d(k)
Next k
End Sub
In this situation I always use code like this (just make sure delimeter you've chosen is not a part of search range)
Dim tmp As String
Dim arr() As String
If Not Selection Is Nothing Then
For Each cell In Selection
If (cell <> "") And (InStr(tmp, cell) = 0) Then
tmp = tmp & cell & "|"
End If
Next cell
End If
If Len(tmp) > 0 Then tmp = Left(tmp, Len(tmp) - 1)
arr = Split(tmp, "|")
Combining the Dictionary approach from Tim with the variant array from Jean_Francois below.
The array you want is in objDict.keys
Sub A_Unique_B()
Dim X
Dim objDict As Object
Dim lngRow As Long
Set objDict = CreateObject("Scripting.Dictionary")
X = Application.Transpose(Range([a1], Cells(Rows.Count, "A").End(xlUp)))
For lngRow = 1 To UBound(X, 1)
objDict(X(lngRow)) = 1
Next
Range("B1:B" & objDict.Count) = Application.Transpose(objDict.keys)
End Sub
This is the old-school way of doing it.
It will execute faster than looping through cells (e.g. For Each cell In Selection) and will be reliable no matter what, as long you have a rectangular selection (i.e. not Ctrl-selecting a bunch of random cells).
Sub FindUnique()
Dim varIn As Variant
Dim varUnique As Variant
Dim iInCol As Long
Dim iInRow As Long
Dim iUnique As Long
Dim nUnique As Long
Dim isUnique As Boolean
varIn = Selection
ReDim varUnique(1 To UBound(varIn, 1) * UBound(varIn, 2))
nUnique = 0
For iInRow = LBound(varIn, 1) To UBound(varIn, 1)
For iInCol = LBound(varIn, 2) To UBound(varIn, 2)
isUnique = True
For iUnique = 1 To nUnique
If varIn(iInRow, iInCol) = varUnique(iUnique) Then
isUnique = False
Exit For
End If
Next iUnique
If isUnique = True Then
nUnique = nUnique + 1
varUnique(nUnique) = varIn(iInRow, iInCol)
End If
Next iInCol
Next iInRow
'// varUnique now contains only the unique values.
'// Trim off the empty elements:
ReDim Preserve varUnique(1 To nUnique)
End Sub
Profiting from the MS Excel 365 function UNIQUE()
In order to enrich the valid solutions above:
Sub ExampleCall()
Dim rng As Range: Set rng = Sheet1.Range("A2:A11") ' << change to your sheet's Code(Name)
Dim a: a = rng
a = getUniques(a)
arrInfo a
End Sub
Function getUniques(a, Optional ZeroBased As Boolean = True)
Dim tmp: tmp = Application.Transpose(WorksheetFunction.Unique(a))
If ZeroBased Then ReDim Preserve tmp(0 To UBound(tmp) - 1)
getUniques = tmp
End Function
OK I did it finally:
Sub CountUniqueRecords()
Dim Array() as variant, UniqueArray() as variant, UniqueNo as Integer,
Dim i as integer, j as integer, k as integer
Redim UnquiArray(1)
k= Upbound(array)
For i = 1 To k
For j = 1 To UniqueNo + 1
If Array(i) = UniqueArray(j) Then GoTo Nx
Next j
UniqueNo = UniqueNo + 1
ReDim Preserve UniqueArray(UniqueNo + 1)
UniqueArray(UniqueNo) = Array(i)
Nx:
Next i
MsgBox UniqueNo
End Sub
one more way ...
Sub get_unique()
Dim unique_string As String
lr = Sheets("data").Cells(Sheets("data").Rows.Count, 1).End(xlUp).Row
Set range1 = Sheets("data").Range("A2:A" & lr)
For Each cel In range1
If Not InStr(output, cel.Value) > 0 Then
unique_string = unique_string & cel.Value & ","
End If
Next
End Sub
This VBA function returns an array of distinct values when passed either a range or a 2D array source
It defaults to processing the first column of the source, but you can optionally choose another column.
I wrote a LinkedIn article about it.
Function DistinctVals(a, Optional col = 1)
Dim i&, v: v = a
With CreateObject("Scripting.Dictionary")
For i = 1 To UBound(v): .Item(v(i, col)) = 1: Next
DistinctVals = Application.Transpose(.Keys)
End With
End Function
The old school method was my favourite option. Thank you. And it was indeed fast. But I didn't use redim. Here though is my real world example where I accumulate values for each unique "key" found in a column and move it into a array (say for an employee and values are hours worked per day). Then I put each key with its final values into a totals area on the active sheet. I've commented extensively for anyone who wants painful detail on what is happening here. Limited error checking is done by this code.
Sub GetActualTotals()
'
' GetActualTotals Macro
'
' This macro accumulates values for each unique employee from the active
' spreadsheet.
'
' History
' October 2016 - Version 1
'
' Invocation
' I created a button labeled "Get Totals" on the Active Sheet that invokes
' this macro.
'
Dim ResourceName As String
Dim TotalHours As Double
Dim TotalPercent As Double
Dim IsUnique As Boolean
Dim FirstRow, LastRow, LastColumn, LastResource, nUnique As Long
Dim CurResource, CurrentRow, i, j As Integer
Dim Resource(1000, 2) As Variant
Dim Rng, r As Range
'
' INITIALIZATIONS
'
' These are index numbers for the Resource array
'
Const RName = 0
Const TotHours = 1
Const TotPercent = 2
'
' Set the maximum number of resources we'll
' process.
'
Const ResourceLimit = 1000
'
' We are counting on there being no unintended data
' in the spreadsheet.
'
' It won't matter if the cells are empty though. It just
' may take longer to run the macro.
' But if there is data where this macro does not expect it,
' assume unpredictable results.
'
' There are some hardcoded values used.
' This macro just happens to expect the names to be in Column C (or 3).
'
' Get the last row in the spreadsheet:
'
LastRow = Cells.Find(What:="*", _
After:=Range("C1"), _
LookAt:=xlPart, _
LookIn:=xlFormulas, _
SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious, _
MatchCase:=False).Row
'
' Furthermore, this macro banks on the first actual name to be in C6.
' so if the last row is row 65, the range we'll work with
' will evaluate to "C6:C65"
'
FirstRow = 6
Rng = "C" & FirstRow & ":C" & LastRow
Set r = Range(Rng)
'
' Initialize the resource array to be empty (even though we don't really
' need to but I'm old school).
'
For CurResource = 0 To ResourceLimit
Resource(CurResource, RName) = ""
Resource(CurResource, TotHours) = 0
Resource(CurResource, TotPercent) = 0
Next CurResource
'
' Start the resource counter at 0. The counter will represent the number of
' unique entries.
'
nUnique = 0
'
' LET'S GO
'
' Loop from the first relative row and the last relative row
' to process all the cells in the spreadsheet we are interested in
'
For i = 1 To LastRow - FirstRow
'
' Loop here for all unique entries. For any
' new unique entry, that array element will be
' initialized in the second if statement.
'
IsUnique = True
For j = 1 To nUnique
'
' If the current row element has a resource name and is already
' in the resource array, then accumulate the totals for that
' Resource Name. We then have to set IsUnique to false and
' exit the for loop to make sure we don't populate
' a new array element in the next if statement.
'
If r.Cells(i, 1).Value = Resource(j, RName) Then
IsUnique = False
Resource(j, TotHours) = Resource(j, TotHours) + _
r.Cells(i, 4).Value
Resource(j, TotPercent) = Resource(j, TotPercent) + _
r.Cells(i,5).Value
Exit For
End If
Next j
'
' If the resource name is unique then copy the initial
' values we find into the next resource array element.
' I ignore any null cells. (If the cell has a blank you might
' want to add a Trim to the cell). Not much error checking for
' the numerical values either.
'
If ((IsUnique) And (r.Cells(i, 1).Value <> "")) Then
nUnique = nUnique + 1
Resource(nUnique, RName) = r.Cells(i, 1).Value
Resource(nUnique, TotHours) = Resource(nUnique, TotHours) + _
r.Cells(i, 4).Value
Resource(nUnique, TotPercent) = Resource(nUnique, TotPercent) + _
r.Cells(i, 5).Value
End If
Next i
'
' Done processing all rows
'
' (For readability) Set the last resource counter to the last value of
' nUnique.
' Set the current row to the first relative row in the range (r=the range).
'
LastResource = nUnique
CurrentRow = 1
'
' Populate the destination cells with the accumulated values for
' each unique resource name.
'
For CurResource = 1 To LastResource
r.Cells(CurrentRow, 7).Value = Resource(CurResource, RName)
r.Cells(CurrentRow, 8).Value = Resource(CurResource, TotHours)
r.Cells(CurrentRow, 9).Value = Resource(CurResource, TotPercent)
CurrentRow = CurrentRow + 1
Next CurResource
End Sub
The VBA script below looks for all unique values from cell B5 all the way down to the very last cell in column B… $B$1048576. Once it is found, they are stored in the array (objDict).
Private Const SHT_MASTER = “MASTER”
Private Const SHT_INST_INDEX = “InstrumentIndex”
Sub UniqueList()
Dim Xyber
Dim objDict As Object
Dim lngRow As Long
Sheets(SHT_MASTER).Activate
Xyber = Application.Transpose(Sheets(SHT_MASTER).Range([b5], Cells(Rows.count, “B”).End(xlUp)))
Sheets(SHT_INST_INDEX).Activate
Set objDict = CreateObject(“Scripting.Dictionary”)
For lngRow = 1 To UBound(Xyber, 1)
If Len(Xyber(lngRow)) > 0 Then objDict(Xyber(lngRow)) = 1
Next
Sheets(SHT_INST_INDEX).Range(“B1:B” & objDict.count) = Application.Transpose(objDict.keys)
End Sub
I have tested and documented with some screenshots of the this solution. Here is the link where you can find it....
http://xybernetics.com/techtalk/excelvba-getarrayofuniquevaluesfromspecificcolumn/
If you don't mind using the Variant data type, then you can use the in-built worksheet function Unique as shown.
sub unique_results_to_array()
dim rng_data as Range
set rng_data = activesheet.range("A1:A10") 'enter the range of data here
dim my_arr() as Variant
my_arr = WorksheetFunction.Unique(rng_data)
first_val = my_arr(1,1)
second_val = my_arr(2,1)
third_val = my_arr(3,1) 'etc...
end sub
If you are not interested in the count function, then you could simplify the dictionary approach by using empty quotes for the dictionary value instead of the counter. The following code assumes the first cell containing data is "A1". Alternatively, you could use the Selection (though I understand that is generally frowned upon) or the sheet's UsedRange attribute as your range.
Both of the following examples assume that you want to omit blank values from your array of unique values.
Note that to utilize dictionary objects as follows, you must have the Microsoft Scripting Runtime library active in your references. Also note that by declaring dict as a New Dictionary instead of a Dictionary in the beginning, you can forgo the step of setting it equal to a Scripting Dictionary later. Also, dictionary keys must be unique, and this method does not result in errors when setting the value corresponding to a given dictionary key, so there is no risk of having unique keys.
Sub GetUniqueValuesInRange()
Dim cll As Range
Dim rng As Range
Dim dict As New Dictionary
Dim vArray As Variant
Set rng = Range("A1").CurrentRegion.Columns(1)
For Each cll In rng.Cells
If Len(cll.Value) > 0 Then
dict(cll.Value) = ""
End If
Next cll
vArray = dict.Keys
End Sub
The prior example is a slower method, as it is generally preferred to move the values into an array in the beginning, so that all calculations can be performed in the memory. The following should work faster for larger data sets:
Sub GetUniqueValuesInRange2()
Dim vFullArray As Variant
Dim var As Variant
Dim dict As New Dictionary
Dim vUniqueArray As Variant
vFullArray = Range("A1").CurrentRegion.Columns(1).Value
For Each var In vFullArray
If Len(var) > 0 Then
dict(var) = ""
End If
Next var
vUniqueArray = dict.Keys
End Sub

Resources