Passing multiple arguments to a function to check duplicate values from an array - excel

I am trying to count duplicate values from a column range using an array, I created a function that takes 3 arguments, number of rows and 2 strings (Long,String,String) for the comparison. I noticed that it only detects duplicates whenever values are next to each other. I already tried looping from 1 to the last row to make sure that each value will be compared to the rest but I got subscript out of range error.
Here's my code:
Sub CountByErrorTest()
Dim rangeArr() As Variant
Dim tester2 As Worksheet
Set tester2 = Worksheets("tester2")
Dim i As Long, j As Long, lrow As Long
lrow = 3169
rangeArr = Worksheets("tester").Range("a2").Resize(lrow, 29).Value2
For i = 1 To 29
For j = 1 To lrow - 1
If dupVal((rangeArr(j, i)), ((rangeArr(j + 1, i)))) Then
tester2.Range("g4") = tester2.Range("g4") + 1
End If
Next j
Next i
End Sub
My function:
Function dupVal(s1 As String, s2 As String) As Boolean
Dim i As Long
If s1 = s2 Then
dupVal = True
Exit Function
End If
dupVal = False
End Function
I tried looping it like this: Gets out of range error
For k = 1 To lrow - 1
If dupVal((rangeArr(j, i)), ((rangeArr(j + k, i)))) Then
tester2.Range("g4") = tester2.Range("g4") + 1
End If
Next k
I would appreciate suggestions to make it better or if there's an easier way to do it that would be really nice.

The duplicates are only being counted when they are next to each other because of this:
dupVal((rangeArr(j, i)), ((rangeArr(j + 1, i))))
The function goes through the entire range and literally compares each column to that column + 1. So, if there was a duplicate 2 or more columns ago, or 1 or more rows ago, the function would not detect it.
The simplest way to detect duplicates is to build a unique list of the values you do know about. Then, when you encounter a new value as you loop through the range, you compare it to that list (which in this case, is an array). If you find it in the list, you count it as a duplicate, because you've seen it before. Otherwise, it's a new value, so add it to the list. In code, this would be:
Function CountByErrorTest() As Long
Const lcol As Long = 29
Const lrow As Long = 3169
Dim uniqueArr() As Variant, find As Variant
Dim tester1 As Worksheet, tester2 As Worksheet
Dim i As Long, j As Long, k As Long, count As Long, uniqueU As Long
Dim found As Boolean
Set tester1 = Worksheets("tester")
Set tester2 = Worksheets("tester2")
uniqueU = -1
count = 0
ReDim uniqueArr(0)
uniqueArr(0) = Null
'Loop through columns
For i = 1 To lcol
'Loop through rows
For j = 1 To lrow
find = tester1.Cells(j, i).Value
found = False
'Loop through array of unique values
For k = LBound(uniqueArr) To UBound(uniqueArr)
'Check whether this is a known value
If (find = uniqueArr(k)) Then
found = True
Exit For
End If
Next k
If found = True Then
'Count the duplicate
count = count + 1
Else
'Add new value to the array
uniqueU = uniqueU + 1
ReDim Preserve uniqueArr(uniqueU)
uniqueArr(uniqueU) = find
End If
Next j
Next i
tester2.Range("g4").Value = count
CountByErrorTest = count
End Function
If you get to a point where you need to do something like this for a very large range, it may start to get slow. To make it more faster, there are data structures which can be used to more efficiently store and retrieve the unique values as you're checking through the range. Using a binary tree instead of an array would be an example of this.
EDIT: Here's an example of how you can generalise this function to accept a range as a parameter and call it from elsewhere:
Function CountByErrorTest(ByRef tester1 As Worksheet, ByRef tester2 As Worksheet, ByRef range As range) As Long
Dim uniqueArr() As Variant, find As Variant
Dim i As Long, j As Long, k As Long, count As Long, uniqueU As Long, lcols As Long, lrows As Long
Dim found As Boolean
lcols = range.Column + range.Columns.count - 1
lrows = range.Row + range.Rows.count - 1
uniqueU = -1
count = 0
ReDim uniqueArr(0)
uniqueArr(0) = Null
'Loop through columns
For i = range.Column To lcols
'Loop through rows
For j = range.Row To lrows
find = tester1.Cells(j, i).Value
found = False
'Loop through array of unique values
For k = LBound(uniqueArr) To UBound(uniqueArr)
'Check whether this is a known value
If (find = uniqueArr(k)) Then
found = True
Exit For
End If
Next k
If found = True Then
'Count the duplicate
count = count + 1
Else
'Add new value to the array
uniqueU = uniqueU + 1
ReDim Preserve uniqueArr(uniqueU)
uniqueArr(uniqueU) = find
End If
Next j
Next i
CountByErrorTest = count
End Function
Sub ExampleCallFunction()
Dim tester1 As Worksheet, tester2 As Worksheet
Dim range As range
Set tester1 = Worksheets("tester")
Set tester2 = Worksheets("tester2")
Set range = tester2.range("b4:c5") 'set what your range is here
tester2.range("g4").Value = CountByErrorTest(tester1, tester2, range)
End Sub

Related

Replace and save remaining string in an array

I want to remove predefined parts of the strings in the following table and save the values in an array. For some reason I get an error stating that I'm outside of the index. The lengths of the strings in the table can vary.
Sub New_1()
Dim i, j, k As Integer
Dim Endings As Variant
k = 0
Endings = Array("/A", "/BB", "/CCC", "/DDDD", "/EEEEE")
Dim ArrayValues() As Variant
With Worksheets("table1")
Dim lastRow As Long: lastRow = .Cells(.Rows.Count, 1).End(xlUp).Row
ReDim ArrayValues(lastRow)
For i = lastRow To 1 Step -1
For j = 0 To UBound(Endings)
ArrayValues(k) = Replace(.Range("A" & i), Endings(j), "")
k = k + 1
Next j
Next i
End With
End Sub
You're getting out of bounds because your ArrValues is filled up after not even 3 iterations of your "i" since you're adding up your k every j iterations
If you want an array of the cleaned up cells do this instead:
Sub New_1()
Dim i As Integer, j As Integer, k As Integer
Dim Endings As Variant
Dim ArrayValues() As Variant
Dim lastRow As Long
Endings = Array("/A", "/BB", "/CCC", "/DDDD", "/EEEEE")
With Worksheets("Blad6")
lastRow = .Cells(.Rows.Count, 1).End(xlUp).Row
ReDim ArrayValues(1 To lastRow) 'Then you don't have an empty ArrayValues(0)
For i = lastRow To 1 Step -1
For j = 0 To UBound(Endings)
If j = 0 Then
ArrayValues(i) = Replace(.Range("A" & i), Endings(j), "")
Else
ArrayValues(i) = Replace(ArrayValues(i), Endings(j), "")
End If
Next j
Next i
'Use Array here
End With
End Sub
If your intent is to create an array in which everything after the / is removed, this might be simpler, using the Split function; and also faster by storing the data to be split in a VBA array, in iterating through that array instead of the worksheet cells.
Option Explicit
Sub New_1()
'in VBA, Long is marginally more efficient than Integer
Dim k As Long, v As Variant
Dim dataArr As Variant
Dim ArrayValues() As Variant
With Worksheets("SHEET7")
'faster to loop through VBA array than worksheet cells
'Note that this will be a 2D array with dimensions starting at 1, not 0
dataArr = Range(.Cells(1, 1), .Cells(.Rows.Count, 1).End(xlUp))
End With
'This might be simpler
ReDim ArrayValues(1 To UBound(dataArr, 1))
k = 0
For Each v In dataArr
k = k + 1
ArrayValues(k) = Split(v, "/")(0)
Next v
End Sub

Pick random names from different lists excel VBA

I would like to pick random names from columns in excel like this :
-In the first sheet "Inscrp" is where the lists are, and the second sheet "Tirage" is where the results of the picking.
-Column A in the sheet "Tirage" should pick random names from column A in the sheet "Inscrp" and the same for the column B, C , till the number of columns I chose
I managed to do this with only the first column and here is the code :
Sub PickNamesAtRandom()
Dim HowMany As Integer
Dim NoOfNames As Long
Dim RandomNumber As Integer
Dim Names() As String 'Array to store randomly selected names
Dim i As Byte
Dim CellsOut As Long 'Variable to be used when entering names onto worksheet
Dim ArI As Byte 'Variable to increment through array indexes
Application.ScreenUpdating = False
HowMany = 5
CellsOut = 8
ReDim Names(1 To HowMany) 'Set the array size to how many names required
NoOfNames = Application.CountA(Worksheets("Inscrp").Range("A3:A100")) - 1 ' Find how many names in the list
i = 1
Do While i <= HowMany
RandomNo:
RandomNumber = Application.RandBetween(3, NoOfNames + 1)
'Check to see if the name has already been picked
For ArI = LBound(Names) To UBound(Names)
If Names(ArI) = Worksheets("Inscrp").Cells(RandomNumber, 1).Value Then
GoTo RandomNo
End If
Next ArI
Names(i) = Worksheets("Inscrp").Cells(RandomNumber, 1).Value ' Assign random name to the array
i = i + 1
Loop
'Loop through the array and enter names onto the worksheet
For ArI = LBound(Names) To UBound(Names)
Worksheets("Tirage").Cells(CellsOut, 1) = Names(ArI)
CellsOut = CellsOut + 1
Next ArI
Application.ScreenUpdating = True
End Sub
Please, test the next code. If I correctly understand your nee, it will extract HowMany random numbers from each column (nrCol) of "Inscrip" sheet and placed starting from CellsOut in sheet "Tirage". The already extracted name is eliminated from the array where it used to exist (to avoid repeated names). The ranges ar placed in arrays and due to that, the code should be very fast mostly working in memory, even for large ranges:
Sub PickNamesAtRandom()
Dim shI As Worksheet, lastR As Long, shT As Worksheet, HowMany As Long
Dim rndNumber As Integer, Names() As String, i As Long, CellsOut As Long
HowMany = 5: CellsOut = 8
Set shI = Worksheets("Inscrp")
Set shT = Worksheets("Tirage")
Dim col As Long, arrCol, filt As String, nrCol As Long
nrCol = 2 'number of columns to be returned. It can be changed and also be calculated...
For col = 1 To nrCol
lastR = shI.cells(shI.rows.count, col).End(xlUp).Row 'last row in column to be processed
If lastR >= HowMany + 2 Then '+ 2 because the range is build starting with the third row...
arrCol = Application.Transpose(shI.Range(shI.cells(3, col), shI.cells(lastR, col)).Value2) 'place the range in a 1D array
ReDim Names(1 To HowMany) 'Set the array size to how many names required
For i = 1 To UBound(Names)
tryAgain:
Randomize
rndNumber = Int((UBound(arrCol) - LBound(arrCol) + 1) * Rnd + LBound(arrCol))
If arrCol(rndNumber) = "" Then GoTo tryAgain
Names(i) = arrCol(rndNumber)
filt = arrCol(rndNumber) & "##$$#": arrCol(rndNumber) = filt
arrCol = filter(arrCol, filt, False) 'eliminate the already used name from the array
Next i
shT.cells(CellsOut, col).Resize(UBound(Names), 1).Value2 = Application.Transpose(Names)
End If
Next col
MsgBox "Ready..."
End Sub
If something unclear, do not hesitate to ask for clarifications...

How to create a function that returns an range

I am looking to create a function that will take 2 ranges (of the same dimensions), and take the difference between the cell from one range and the corresponding cell in the other range, and then create a new range with all of the differences. Are there any obvious problems? If i select and crtl + sht + enter, the range fills with "#Value!"
This is what i have so far (assuming the ranges are 4 by 4s):
Function Compare_Ranges(range_1 As Range, range_2 As Range) As Range
Dim output_data As Range
Dim i As Integer
Dim j As Integer
Dim col As String
For i = 1 To 4 'looping through the columns
col = Col_Letter(i)
For j = 1 To 4 'looping through the rows
Set output_data(Col_Letter(i) & j) = range_1(Col_Letter(i) & j).Value - range_2(Col_Letter(i) & j).Value
Next j
Next i
Compare_Ranges = output_data
End Function
Where the function Col_Letter returns the correponding letter of the alphabet:
Function Col_Letter(lngCol As Integer) As String
Dim vArr
vArr = Split(Cells(1, lngCol).Address(True, False), "$")
Col_Letter = vArr(0)
End Function
Here is a version of your function that takes two ranges of the same size and returns an array with the same dimensions that holds the difference between each corresponding cell in the input ranges.
Function Compare_Ranges(range_1 As Range, range_2 As Range) As Variant
Dim output_data() As Variant
Dim c As Integer
Dim r As Integer
ReDim output_data(1 To range_1.Rows.Count, 1 To range_1.Columns.Count)
For c = 1 To UBound(output_data, 2) 'looping through the columns
For r = 1 To UBound(output_data, 1) 'looping through the rows
output_data(r, c) = range_1.Cells(r, c).Value - range_2.Cells(r, c).Value
Next
Next
Compare_Ranges = output_data
End Function
If you want to put this in a cell, you will need to press CTRL+ENTER after entiering the following in a cell:
=Compare_Ranges(A1:A7,B1:B7)
The function returns an array, so if you want to catch it's results by calling it in another sub procedure, you need the following"
Dim data as variant
data = Compare_Ranges(range("a1:a7"),range("b1:b7"))
I am not sure if I got this right but I hope at least will help you to get there. The function takes any two ranges and calculate the difference between them and store the result into an array.
Function Compare_Ranges(range_1 As Range, range_2 As Range, ByVal y As Long) As Variant
Dim j As Long
Dim col As String
Dim one As Object, two As Object, three As Variant
Set one = CreateObject("Scripting.Dictionary")
Set two = CreateObject("Scripting.Dictionary")
j = 0
For Each cell In range_1
one.Add Key:=j, Item:=cell.Value
j = j + 1
Next
j = 0
For Each cell In range_2
two.Add j, cell.Value
j = j + 1
Next
ReDim three(0 To j) As Variant
For i = 0 To j
three(i) = one(i) - two(i)
Next
Compare_Ranges = three(y)
End Function
Then you can use the code in the sub to populate them in any range you like.
Sub result()
Dim one As Range, two As Range, three As Range
Dim j As Long
Set one = Worksheets("Sheet1").Range("A1:A4")
Set two = Worksheets("Sheet1").Range("B1:B4")
Set result = Worksheets("Sheet1").Range("D8:D11")
j = 0
For i = three.Row To ((result.Row + result.Rows.Count) - 1)
Worksheets("Sheet1").Cells(i, result.Column) = Compare_Ranges(one, two, j)
j = j + 1
Next
End Sub

Do a loop with multiple constant condition VBA

I am trying to do a loop but I'm a little stuck.
Sub Macro()
Range("A392: A401").Value = Range("N2")
Range("A402: A411").Value = Range("N3")
Range("A412: A421").Value = Range("N4")
Range("A422: A431").Value = Range("N5")
....
I need to repeat this logic ( On column A to set a value for each 10 rows) this value will be from Column N from 1 to 1 until it finds an empty row ...
I'm not being able to do the loop with these multiples conditions, would you please help me ?
Thanks a lot!
Range.Offset is a great method to manipulate ranges. Using it, we can automate the ranges to move down the sheet with each loop.
Sub Macro()
Dim i As Long
While Range("N2").Offset(i) <> ""
'Offset will shift N2 down by one each loop
'Offset will shift the 10 cell range down by 10 on each loop
Range("A392: A401").Offset(i * 10).Value = Range("N2").Offset(i)
i = i + 1
Wend
End Sub
I'm not quite sure what's your actual intention:
Repeat all values in column N2:N5 10 times and append the whole data block to the first free cell in column A.
Repeat all non-empty values in column N 10 times and write them to a fixed target starting with cell A392.
In both cases you can prefill an array and write it to the defined target in column A. Looping through an array has some speed advantages whereas looping through a range by means of VBA can be time consuming.
The direct (untested) copying of whole blocks as shown by #Toddleson can improve this behaviour, depending on total range sizes.
Case 1
Sub Example1()
Const RowsCount As Long = 10
With Sheet1 ' << change to your project's sheet (Code)Name
'get values to repeat & count them
Dim vals: vals = .Range("N2:N5")
Dim cnt As Long: cnt = UBound(vals)
'provide for 1-based 2-dim results array
Dim results
ReDim results(1 To RowsCount * cnt, 1 To 1)
'fill array with repeated values
Dim i As Long, j As Long
For i = 1 To cnt
For j = 1 To RowsCount
results((i - 1) * RowsCount + j, 1) = vals(i, 1)
Next j
Next i
'append above data block
Dim nxtRow As Long
nxtRow = .Range("A" & .Rows.Count).End(xlUp).Row + 1
.Range("A" & nxtRow).Resize(UBound(results), 1) = results
End With
End Sub
Case 2
Sub Example2()
Const RowsCount As Long = 10
With Sheet1 ' << change to your project's sheet (Code)Name
'get values to repeat & count them
Dim lastRow As Long
lastRow = .Range("N" & .Rows.Count).End(xlUp).Row
Dim vals: vals = .Range("N2:N" & lastRow)
Dim ValsCount As Long: ValsCount = UBound(vals)
'provide for 1-based 2-dim results array
Dim results
ReDim results(1 To RowsCount * ValsCount, 1 To 1)
'fill array with repeated values
Dim i As Long, j As Long, ii As Long
For i = 1 To ValsCount
If Len(vals(i, 1)) > 0 Then ' check if non-empty value in column N
ii = ii + 1
For j = 1 To RowsCount
results((ii - 1) * RowsCount + j, 1) = vals(i, 1)
Next j
End If
Next i
'write data block to fixed target starting with A392
.Range("A392").Resize(UBound(results), 1) = results
End With
End Sub

Remove duplicates from array using VBA

Assume I have a block of data in Excel 2010, 100 rows by 3 columns.
Column C contains some duplicates, say it starts off as
1, 1, 1, 2, 3, 4, 5, ..... , 97, 98
Using VBA, I would like to remove the duplicate rows so I am left with 98 rows and 3 columns.
1, 2, 3, ..... , 97, 98
I know there is a button in Excel 2010 to do that but it inteferes with the rest of my code subsequently and gives incorrect results.
Furthermore, I would like to do it in arrays, then paste the results on the worksheet, rather than methods such as Application.Worksheetfunction.countif(.....
So something like:
Dim myarray() as Variant
myarray=cells(1,1).Currentregion.value
Dim a as Long
For a=1 to Ubound(myarray,1)
'something here to
Next a
I answered a similar question. Here is the code I used:
Dim dict As Object
Dim rowCount As Long
Dim strVal As String
Set dict = CreateObject("Scripting.Dictionary")
rowCount = Sheet1.Range("A1").CurrentRegion.Rows.Count
'you can change the loop condition to iterate through the array rows instead
Do While rowCount > 1
strVal = Sheet1.Cells(rowCount, 1).Value2
If dict.exists(strVal) Then
Sheet1.Rows(rowCount).EntireRow.Delete
Else
'if doing this with an array, then add code in the Else block
' to assign values from this row to the array of unique values
dict.Add strVal, 0
End If
rowCount = rowCount - 1
Loop
Set dict = Nothing
If you want to use an array, then loop through the elements with the same conditional (if/else) statements. If the item doesn't exist in the dictionary, then you can add it to the dictionary and add the row values to another array.
Honestly, I think the most efficient way is to adapt code you'd get from the macro recorder. You can perform the above function in one line:
Sheet1.UsedRange.RemoveDuplicates Columns:=3, Header:=xlYes
Function eliminateDuplicate(poArr As Variant) As Variant
Dim poArrNoDup()
dupArrIndex = -1
For i = LBound(poArr) To UBound(poArr)
dupBool = False
For j = LBound(poArr) To i
If poArr(i) = poArr(j) And Not i = j Then
dupBool = True
End If
Next j
If dupBool = False Then
dupArrIndex = dupArrIndex + 1
ReDim Preserve poArrNoDup(dupArrIndex)
poArrNoDup(dupArrIndex) = poArr(i)
End If
Next i
eliminateDuplicate = poArrNoDup
End Function
Simple function to remove duplicates from a 1D array
Private Function DeDupeArray(vArray As Variant) As Variant
Dim oDict As Object, i As Long
Set oDict = CreateObject("Scripting.Dictionary")
For i = LBound(vArray) To UBound(vArray)
oDict(vArray(i)) = True
Next
DeDupeArray = oDict.keys()
End Function
Edit:
With stdVBA (a library largely maintained by myself) you can use:
uniqueValues = stdEnumerator.CreateFromArray(myArray).Unique().AsArray()
An improvement on #RBILLC and #radoslav006 answers, this version searches the array with the duplicates removed for existing values so it searchs less values to find a duplicate.
Public Function RemoveDuplicatesFromArray(sourceArray As Variant)
Dim duplicateFound As Boolean
Dim arrayIndex As Integer, i As Integer, j As Integer
Dim deduplicatedArray() As Variant
arrayIndex = -1
deduplicatedArray = Array(1)
For i = LBound(sourceArray) To UBound(sourceArray)
duplicateFound = False
For j = LBound(deduplicatedArray) To UBound(deduplicatedArray)
If sourceArray(i) = deduplicatedArray(j) Then
duplicateFound = True
Exit For
End If
Next j
If duplicateFound = False Then
arrayIndex = arrayIndex + 1
ReDim Preserve deduplicatedArray(arrayIndex)
deduplicatedArray(arrayIndex) = sourceArray(i)
End If
Next i
RemoveDuplicatesFromArray = deduplicatedArray
End Function
Here's another approach for working with an array:
Sub tester()
Dim arr, arrout
arr = Range("A1").CurrentRegion.Value 'collect the input array
arrout = UniqueRows(arr) 'get only unique rows
Range("H1").Resize(UBound(arrout, 1), UBound(arrout, 2)).Value = arrout
End Sub
Function UniqueRows(arrIn As Variant) As Variant
Dim keys, rw As Long, col As Long, k, sep, arrout
Dim dict As Object, lbr As Long, lbc As Long, ubr As Long, ubc As Long, rwOut As Long
Set dict = CreateObject("scripting.dictionary")
'input array bounds
lbr = LBound(arrIn, 1)
ubr = UBound(arrIn, 1)
lbc = LBound(arrIn, 2)
ubc = UBound(arrIn, 2)
ReDim keys(lbr To ubr)
'First pass:collect all the row "keys" in an array
' and unique keys in a dictionary
For rw = lbr To ubr
k = "": sep = ""
For col = lbc To ubc
k = k & sep & arrIn(rw, col)
sep = Chr(0)
Next col
keys(rw) = k 'collect key for this row
dict(k) = True 'just collecting unique keys
Next rw
'Resize output array to # of unique rows
ReDim arrout(lbr To dict.Count + (lbr - 1), lbc To ubc)
rwOut = lbr
'Second pass: copy each unique row to the output array
For rw = lbr To ubr
If dict(keys(rw)) Then 'not yet output?
For col = lbc To ubc 'copying this row over to output...
arrout(rwOut, col) = arrIn(rw, col)
Next col
rwOut = rwOut + 1 'increment output "row"
dict(keys(rw)) = False 'flag this key as copied
End If
Next rw
UniqueRows = arrout
End Function
Answer from #RBILLC could be easily improved by adding an Exit For inside internal loop:
Function eliminateDuplicate(poArr As Variant) As Variant
Dim poArrNoDup()
dupArrIndex = -1
For i = LBound(poArr) To UBound(poArr)
dupBool = False
For j = LBound(poArr) To i
If poArr(i) = poArr(j) And Not i = j Then
dupBool = True
Exit For
End If
Next j
If dupBool = False Then
dupArrIndex = dupArrIndex + 1
ReDim Preserve poArrNoDup(dupArrIndex)
poArrNoDup(dupArrIndex) = poArr(i)
End If
Next i
eliminateDuplicate = poArrNoDup
End Function
I think this is really a case for using excel's native functions, at least for the initial array acquisition, and I don't think there's any simpler way to do it. This sub will output the unique values starting in column 5. I assumed that the target range was empty, so if it's not, change r and c.
Sub testUniques()
Dim arr, r As Long, c As Long, h As Long, w As Long
Dim this As Worksheet: Set this = ActiveSheet
arr = Application.Unique(this.Cells(1, 1).CurrentRegion)
r = 1
c = 5
h = UBound(arr, 1) - 1
w = UBound(arr, 2) - 1
this.Range(this.Cells(r, c), this.Cells(r + h, c + w)) = arr
End Sub
I know this is old, but here's something I used to copy duplicate values to another range so that I could see them quickly to establish data integrity for a database I was standing up from various spreadsheets. To make the procedure delete the duplicates it would be as simple as replacing the dupRng lines with Cell.Delete Shift:=xlToLeft or something to that effect.
I haven't tested that personally, but it should work.
Sub PartCompare()
Dim partRng As Range, partArr() As Variant, i As Integer
Dim Cell As Range, lrow As Integer
lrow = ThisWorkbook.Worksheets("Sheet1").Cells(Rows.Count, 1).End(xlUp).Row
i = 0
Set partRng = ThisWorkbook.Worksheets("Sheet1").Range(Cells(1, 1), Cells(lrow, 1))
For Each Cell In partRng.Cells
ReDim Preserve partArr(i)
partArr(i) = Cell.Value
i = i + 1
Next
Dim dupRng As Range, j As Integer, x As Integer, c As Integer
Set dupRng = ThisWorkbook.Worksheets("Sheet1").Range("D1")
x = 0
c = 1
For Each Cell In partRng.Cells
For j = c To UBound(partArr)
If partArr(j) = Cell.Value Then
dupRng.Offset(x, 0).Value = Cell.Value
dupRng.Offset(x, 1).Value = Cell.Address()
x = x + 1
Exit For
End If
Next j
c = c + 1
Next Cell
End Sub
Remove duplicates (plus related row items) from array
As OP wanted a VBA solution close to RemoveDuplicates, I demonstrate an array approach using a â–ºdictionary to get not the unique items per se (dict.keys), but the related row indices of first occurrencies (dict.items).
These are used to retain the whole row data via procedure LeaveUniques profiting from the advanced possibilities of the â–ºApplication.Index() function - c.f. Some peculiarities of the the Application.Index function
Example Call
Sub ExampleCall()
'[0]define range and assign data to 1-based 2-dim datafield
With Sheet1 ' << reference to your project's sheet Code(Name)
Dim lastRow: lastRow = .Cells(.Rows.Count, "C").End(xlUp).Row
Dim rng: Set rng = .Range("C2:E" & lastRow)
End With
Dim data: data = rng ' assign data to 2-dim datafield
'[1]get uniques (column 1) and remove duplicate rows
LeaveUniques data ' << call procedure LeaveUniques (c.f. RemoveDuplicates)
'[2]overwrite original range
rng.Clear
rng.Resize(UBound(data), UBound(data, 2)) = data
End Sub
Procedure LeaveUniques
Sub LeaveUniques(ByRef data As Variant, Optional ByVal colNum As Long = 1)
'Purpose: procedure removes duplicates of given column number in entire array
data = Application.Index(data, uniqueRowIndices(data, colNum), nColIndices(UBound(data, 2)))
End Sub
Help functions to LeaveUniques
Function uniqueRowIndices(data, Optional ByVal colNum As Long = 1)
'Purpose: return data index numbers referring to uniques
'a) set late bound dictionary to memory
Dim dict As Object
Set dict = CreateObject("Scripting.Dictionary")
'b) slice e.g. first data column (colNum = 1)
Dim colData
colData = Application.Index(data, 0, colNum)
'c) fill dictionary with uniques referring to first occurencies
Dim i As Long
For i = 1 To UBound(colData)
If Not dict.exists(dict(colData(i, 1))) Then dict(colData(i, 1)) = i
Next
'd) return 2-dim array of valid unique 1-based index numbers
uniqueRowIndices = Application.Transpose(dict.items)
End Function
Function nColIndices(ByVal n As Long)
'Purpose: return "flat" array of n column indices, e.g. for n = 3 ~> Array(1, 2, 3)
nColIndices = Application.Transpose(Evaluate("row(1:" & n & ")"))
End Function

Resources