Separate duplicates and output to other worksheets - vba

I tried to make a program that separates the duplicates and outputs to other worksheets. The data is composed of 3 columns and thousands of rows. First column is a unique number, second column is the material name, and third is the description of the material.
I tried:
Sub duplicates_separation()
Dim duplicate(), i As Long
Dim delrange As Range, cell As Long
Dim delrange2 As Range
Dim shtIn As Worksheet, shtOut As Worksheet
Set shtIn = ThisWorkbook.Sheets("process")
Set shtOut = ThisWorkbook.Sheets("output")
x = 2
y = 1
Set delrange = Range("b1:b30000") 'set your range here
Set delrange2 = Range("c1:c30000")
For cell = 1 To delrange.Cells.Count
If Application.CountIf(delrange, delrange(cell)) > 1 Then
ReDim Preserve duplicate(i)
duplicate(i) = delrange(cell).Address
i = i + 1
End If
Next
For i = UBound(duplicate) To LBound(duplicate) Step -1
Range(duplicate(i)).Value = shtOut.Cells(x, 1).Value
x = x + 1
Next i
End Sub
But it goes to Error 9, Out of Range. It worked before, I don't know why it goes to an error. Anybody know why?
UPDATE
error happen in For i = UBound(duplicate) To LBound(duplicate) Step -1
what i chaged :
from
range(duplicate(i)).entirerow.cut
shtout.cells(x,1).paste
to
Range(duplicate(i)).Value = shtOut.Cells(x, 1).Value
UPDATE It's works now!
Sub duplicates_separation()
Dim duplicate(), i As Long
Dim delrange As Range, cell As Long
Dim delrange2 As Range
Dim shtIn As Worksheet, shtOut As Worksheet
Set shtIn = ThisWorkbook.Sheets("process")
Set shtOut = ThisWorkbook.Sheets("output")
x = 2
y = 1
Set delrange = shtIn.Range("b1:b30000") 'set your range here
Set delrange2 = shtIn.Range("c1:c30000")
'search duplicates in 2nd column
For cell = 1 To delrange.Cells.Count
If Application.CountIf(delrange, delrange(cell)) > 1 Then
ReDim Preserve duplicate(i)
duplicate(i) = delrange(cell).Address
i = i + 1
End If
Next
'search duplicates in 3rd column
For cell = 1 To delrange2.Cells.Count
If Application.CountIf(delrange2, delrange2(cell)) > 1 Then
ReDim Preserve duplicate(i)
duplicate(i) = delrange2(cell).Address
i = i + 1
End If
Next
'add header
shtOut.Cells(1, 1).Resize(1, 3).Value = _
Array("Material Number", "Short Description", "Long Description")
'print duplicates
For i = UBound(duplicate) To LBound(duplicate) Step -1
shtOut.Cells(x, 1).EntireRow.Value = shtIn.Range(duplicate(i)).EntireRow.Value
x = x + 1
Next i
End Sub

This works if you run the code while the sheet that contains the data is selected.
If not, and the sheet currently selected have no value in b1:b30000, then this line:
For i = UBound(duplicate) To LBound(duplicate) Step -1
will produce the Error 9 since you were not able to initialize duplicate variable since you only Redim Preserve when your If statement was satisfied.
To avoid the error, properly declare the variable delrange like this:
Set delrange = shtIn.Range("b1:b30") 'i assumed only that shtIn is the source sheet, change otherwise.
Set delrange2 = shtIn.Range("c1:c30")
and then change this line as well:
For i = UBound(duplicate) To LBound(duplicate) Step -1
shtOut.Cells(x, 1).Value = shtIn.Range(duplicate(i)).Value
x = x + 1
Next i
Again, i assumed that you are writing on shtOut and not the other way around like what you did in your code.

Related

Macro to move rows to another sheet based on cell value

The below macros works for - I have a workbook with two sheets (Active and Archive). And in Active sheet i have AB column that contains Active or Archive status. If its status Archive macros cuts and moves the row to the sheet Archive. This macros works perfect.
Now i need to add some other sheets to excel and named them (New, Accepted, Rejected) and of course i add the same status to the column AB. Now i want macros to do the same if AB = Archive or New or Accepted or Rejected cut and move the row to the sheet named Archive or New or Accepted or Rejected.
I tried it by myself but can't do it.
Need ur help. Thanks in advance.
Private Sub CommandButton1_Click()
Dim x As Integer
Dim y As Integer
Dim i As Integer
Dim shSource As Worksheet
Dim shTarget1 As Worksheet
Set shSource = ThisWorkbook.Sheets("Active")
Set shTarget1 = ThisWorkbook.Sheets("Archive")
If shTarget1.Cells(2, 28).Value = "" Then
x = 2
Else
x = shTarget1.Cells(2, 28).CurrentRegion.Rows.Count + 1
End If
i = 2
Do Until shSource.Cells(i, 28) = ""
If shSource.Cells(i, 28).Value = "Archive" Then
shSource.Rows(i).Copy
shTarget1.Cells(x, 1).PasteSpecial Paste:=xlPasteValues
shSource.Rows(i).Delete
x = x + 1
GoTo Line1
End If
i = i + 1
Line1: Loop
End Sub
You can set up multiple variables and choose the right ones in a select case. There is some repetition here that could get cleaned up with arrays.
Sub CommandButton1_Click()
Dim x As Integer 'archive target counter
Dim y As Integer 'new target counter
Dim z As Integer 'accepted target counter
Dim w As Integer 'rejected target counter
'the above could be an array if we were trying to generalize
Dim i As Integer 'counts rows in original sheet
Dim shSource As Worksheet
Dim shTarget1 As Worksheet 'archive sheet
Dim shTarget2 As Worksheet 'new sheet
Dim shTarget3 As Worksheet 'accepted sheet
Dim shTarget4 As Worksheet 'rejected sheet
'these 4 could also be an array, as could their names, in which case some things become loops and the select case could be written out
Set shSource = ThisWorkbook.Sheets("Active")
Set shTarget1 = ThisWorkbook.Sheets("Archive")
Set shTarget2 = ThisWorkbook.Sheets("New")
Set shTarget3 = ThisWorkbook.Sheets("Accepted")
Set shTarget4 = ThisWorkbook.Sheets("Rejected")
If shTarget1.Cells(2, 28).Value = "" Then
x = 2
Else
x = shTarget1.Cells(2, 28).CurrentRegion.Rows.Count + 1
End If
If shTarget2.Cells(2, 28).Value = "" Then
y = 2
Else
y = shTarget2.Cells(2, 28).CurrentRegion.Rows.Count + 1
End If
If shTarget3.Cells(2, 28).Value = "" Then
z = 2
Else
z = shTarget3.Cells(2, 28).CurrentRegion.Rows.Count + 1
End If
If shTarget4.Cells(2, 28).Value = "" Then
w = 2
Else
w = shTarget4.Cells(2, 28).CurrentRegion.Rows.Count + 1
End If
i = 2
Do Until shSource.Cells(i, 28) = ""
Select Case shSource.Cells(i, 28).Value
Case "Archive":
shSource.Rows(i).Copy
shTarget1.Cells(x, 1).PasteSpecial Paste:=xlPasteValues
shSource.Rows(i).Delete
x = x + 1
Case "New":
shSource.Rows(i).Copy
shTarget2.Cells(y, 1).PasteSpecial Paste:=xlPasteValues
shSource.Rows(i).Delete
y = y + 1
Case "Accepted":
shSource.Rows(i).Copy
shTarget3.Cells(z, 1).PasteSpecial Paste:=xlPasteValues
shSource.Rows(i).Delete
z = z + 1
Case "Rejected":
shSource.Rows(i).Copy
shTarget4.Cells(w, 1).PasteSpecial Paste:=xlPasteValues
shSource.Rows(i).Delete
w = w + 1
Case Else 'no cutting so move to next input line
i = i + 1
End Select
Loop
End Sub
EDIT: Below is the array based version that repeats itself less. Also, I found I kept overwriting my top row in the target sheets, so I added 2 (not 1) to the target counters when I initialized them. If the original was working in your context, you may switch it back.
Sub CommandButton1_Click()
Dim TargetCounters(3) As Integer
Dim TargetNames(3) As String
TargetNames(0) = "Archive"
TargetNames(1) = "New"
TargetNames(2) = "Accepted"
TargetNames(3) = "Rejected"
Dim i As Integer 'counts rows in original sheet
Dim shSource As Worksheet
Dim shTargets(3) As Worksheet
Set shSource = ThisWorkbook.Sheets("Active")
For i = 0 To 3
Set shTargets(i) = ThisWorkbook.Sheets(TargetNames(i))
If shTargets(i).Cells(2, 28).Value = "" Then
TargetCounters(i) = 2
Else 'there is stuff. Imagine for example it is in rows 2 to 7. Count will be 6. We need to start pasting in row 8
TargetCounters(i) = shTargets(i).Cells(2, 28).CurrentRegion.Rows.Count + 2 'changed this from orinal + 1
End If
Next i
i = 2
Dim MatchIndex As Integer
Do Until shSource.Cells(i, 28).Value = ""
'you could switch this case to a call on the application's match function against TargetNames
'if you take care with the case where it is not found and indexing being right and not off by 1
Select Case shSource.Cells(i, 28).Value
Case "Archive":
MatchIndex = 0
Case "New":
MatchIndex = 1
Case "Accepted":
MatchIndex = 2
Case "Rejected":
MatchIndex = 3
Case Else 'no cutting so set signal and we will move to next input line
MatchIndex = -1
End Select
If (MatchIndex = -1) Then
i = i + 1
Else
shSource.Rows(i).Copy
shTargets(MatchIndex).Cells(TargetCounters(MatchIndex), 1).PasteSpecial Paste:=xlPasteValues
shSource.Rows(i).Delete
TargetCounters(MatchIndex) = TargetCounters(MatchIndex) + 1
End If
Loop
End Sub

Excel VBA copying specific columns

I have the following VBA code for excel
Dim k As Integer, z As Integer
Dim sourceSht As Worksheet
Dim destSht As Worksheet
z = 0
Set sourceSht = Sheets("sheet1")
Set destSht = Sheets("sheet2")
DoEvents
For k = 1 To 5000
If k < 3 Or (k - 1) Mod 3 <> 0 Then
z = z + 1
sourceSht.Columns(k).Copy destSht.Columns(z)
End If
Next
This code was working perfectly for rows (changed this part"sourceSht.Columns(k).Copy destSht.Columns(z)").
but I can not make it work for columns. I want excel to copy the first 2 columns then skip the third one, then copy 2 again, skip one and etc... can somebody help me and explain what am I doing wrong?
I'm going to ignore the use of mod and do a Step 3 with the loop:
Dim i as Long, j as Long
For i = 1 to 5000 Step 3
With sourceSht
If j = 0 Then
j = 1
Else
j = j + 2 'Copying 2 columns over, so adding 2 each time
End If
.Range(.Columns(i),.Columns(i+1)).Copy destSht.Range( destSht.Columns(j), destSht.Column(j+1))
End With
Next i
Something like that should do it for you
Alternate:
Sub tgr()
Dim wsSource As Worksheet
Dim wsDest As Worksheet
Dim rCopy As Range
Dim rLast As Range
Dim LastCol As Long
Dim i As Long
Set wsSource = ActiveWorkbook.Sheets("Sheet1")
Set wsDest = ActiveWorkbook.Sheets("Sheet2")
On Error Resume Next
Set rLast = wsSource.Cells.Find("*", wsSource.Range("A1"), xlFormulas, , xlByColumns, xlPrevious)
On Error GoTo 0
If rLast Is Nothing Then Exit Sub 'No data
LastCol = rLast.Column
Set rCopy = wsSource.Range("A:B")
For i = 4 To LastCol Step 3
Set rCopy = Union(rCopy, wsSource.Columns(i).Resize(, 2))
Next i
rCopy.Copy wsDest.Range("A1")
End Sub
Try this (use count for the number of time you need to copy columns, t for the first columns you need to copy):
Sub copy_columns()
t = 1
Count = 1
Do Until Count = 10
Range(Columns(t), Columns(t + 1)).Copy
Cells(1, t + 3).Select
Selection.PasteSpecial Paste:=xlPasteValues
t = t + 3
Count = Count + 1
Loop
End Sub

Auto scheduling

I am trying to make an auto scheduling program with an excel.
For example, each number is certain job assigned to the person given day.
1/2 1/3 1/4 1/5
Tom 1 2 2 ?
Justin 2 3 1 ?
Mary 3 3 ?
Sam 1 ?
Check O O X ? ## check is like =if(b2=c2,"O","X")
The things I want to make sure is every person is given a different job from yesterday.
My idea
while
randomly distribute jobs for 1/5
wend CheckCell = "O"
But I found that checking cell in the vba script doesn't work - the cell is not updated in each while loop.
Could you give me a little pointer for these kinds of program? Because I am new to vbaScript, any kinds of help would be appreciated.
Using VBA, I'm sure there are better ways to do this, but this will check the values from the penultimate column against values from last column and if they match it will write "O" to under the last column, else it will write "X":
Sub foo()
Dim ws As Worksheet: Set ws = Sheets("Sheet1")
'declare and set your worksheet, amend as required
LastRow = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row
'get the last row with data on Column A
LastCol = ws.Cells(1, ws.Columns.Count).End(xlToLeft).Column
counter = 0 'set counter
For i = 2 To LastRow 'loop through penultimate column and add values to array
If ws.Cells(i, LastCol - 1).Value <> "" Then
Values = Values & ws.Cells(i, LastCol - 1) & ","
End If
Next i
Values = Left(Values, Len(Values) - 1)
Values = Split(Values, ",") 'split values into array
For i = 2 To LastRow 'loop through last column and add values to array
If ws.Cells(i, LastCol).Value <> "" Then
ValuesCheck = ValuesCheck & ws.Cells(i, LastCol) & ","
End If
Next i
ValuesCheck = Left(ValuesCheck, Len(ValuesCheck) - 1)
ValuesCheck = Split(ValuesCheck, ",")
For y = LBound(Values) To UBound(Values) 'loop through both arrays to find all values match
For x = LBound(ValuesCheck) To UBound(ValuesCheck)
If Values(y) = ValuesCheck(x) Then counter = counter + 1
Next x
Next y
If counter = UBound(Values) + 1 Then 'if values match
ws.Cells(LastRow + 1, LastCol).Value = "O"
Else 'else write X
ws.Cells(LastRow + 1, LastCol).Value = "X"
End If
End Sub
just to clarify are you looking to implement the random number in the vba or the check.
To do the check the best way would be to set the area as a range and then check each using the cells(r,c) code, like below
Sub checker()
Dim rng As Range
Dim r As Integer, c As Integer
Set rng = Selection
For r = 1 To rng.Rows.Count
For c = 1 To rng.Columns.Count
If rng.Cells(r, c) = rng.Cells(r, c + 1) Then
rng.Cells(r, c).Interior.Color = RGB(255, 0, 0)
End If
Next c
Next r
End Sub
this macro with check the text you have selected for the issue and change the cell red if it matches the value to the right.
To make it work for you change set rng = selection to your range and change the rng.Cells(r, c).Interior.Color = RGB(255, 0, 0) to the action you want
A sligthly different approach than the other answers.
Add this function:
Function PickJob(AvailableJobs As String, AvoidJob As String)
Dim MaxTries As Integer
Dim RandomJob As String
Dim Jobs() As String
Jobs = Split(AvailableJobs, ",")
MaxTries = 100
Do
MaxTries = MaxTries - 1
If MaxTries = 0 Then
MsgBox "Could find fitting job"
End
End If
RandomJob = Jobs(Int((1 + UBound(Jobs)) * Rnd()))
Loop Until RandomJob <> AvoidJob
PickJob = RandomJob
End Function
And put this formula in your sheet
=PickJob("1,2,3",D2)
where D2 points to is the previous job

Excel VBA - Loop to next available blank cell

I am attempting to write a small section of code to create a new worksheet and insert values from a table in a source worksheet starting at row 2, column 1 thru column 4. Once it reaches the end, I need it to loop to the next row and start over.
The issue I have is that the below code loops back to row 1 of the new worksheet and data is overridden. Is there a simple way to have my loop start on the first blank row down?
[2
Sub SAX()
Dim wsSource As Excel.Worksheet, wsData As Excel.Worksheet
Dim r As Long, c As Long
Set wsData = ThisWorkbook.Sheets.Add(After:=ThisWorkbook.Sheets(ThisWorkbook.Sheets.Count))
wsData.Name = "Data"
Set wsSource = ThisWorkbook.Worksheets("Header")
Application.DisplayAlerts = False
r = 2
Do Until Len(Trim(wsSource.Cells(r, 1).Value)) = 0
For c = 1 To 4
wsData.Cells(c * 1, 1).Value = wsSource.Cells(r, c).Value
Next c
ThisWorkbook.Activate
r = r + 1
Loop
Application.DisplayAlerts = True
End Sub
What you want is this, assuming (from screenshot) that you're working with a structured ListObject table:
Sub SAX()
Dim wsSource As Excel.Worksheet, wsData As Excel.Worksheet
Dim i as Long
Dim tbl As ListObject
Dim vals As Variant
With ThisWorkbook
Set wsData = Sheets.Add(After:=.Sheets(.Sheets.Count))
Set wsSource = .Worksheets("Header")
End With
wsData.Name = "Data"
'## Get a handle on the Table object
Set tbl = wsSource.ListObjects(1) 'Modify if needed
Application.DisplayAlerts = False
i = 1 'which row we start putting data on wsData
'## Iterate each row of data in the Table
For Each rng In tbl.DataBodyRange.Rows
'## Dump this row's values in to an array, and transpose it
vals = Application.Transpose(rng.Value)
'## Put the array's values in an appropriately sized range on the wsData sheet:
wsData.Cells(i, 1).Resize(UBound(vals)).Value = vals
'## Increment the destination row number:
i = i + UBound(vals)
Next
Application.DisplayAlerts = True
End Sub
Here we transpose the rng.Value so that we can drop it in a column. We store this in the vals array. We then use the vals array to determine the size of the range where the values will be placed on "Data" sheet, and also use the size of the vals array to increment our i variable, which tells us where to put the next row's data.
Or, maybe even more simply:
For i = 1 to tbl.DataBodyRange.Cells.Count
wsData.Cells(i, 1).Value = tbl.DataBodyRange.Cells(i).Value
Next
This works because a range is indexed by row/column, so we begin counting cell #1 at the top/left, and then wrap to the second row and resume counting, for example, the "cell index" is in this example table:
This can easily be put into a single row or column, just by iterating over the Cells.Count!
Try this...you actually need two Row values, one for data, one for output:
Sub SAX()
Dim wsSource As Worksheet, wsData As Worksheet
Dim lDataRow As Long, lCol As Long, lOut as Long
Set wsData = ThisWorkbook.Sheets.Add(After:=ThisWorkbook.Sheets(ThisWorkbook.Sheets.Count))
wsData.Name = "Data"
Set wsSource = ThisWorkbook.Worksheets("Header")
Application.DisplayAlerts = False
lDataRow = 2
lOut = 1
Do
For lCol = 1 To 4
wsData.Cells(lOut, 1) = wsSource.Cells(lDataRow, lCol)
Next lCol
lDataRow = lDataRow + 1
lOut = lOut + 1
Loop Until Len(Trim(wsSource.Cells(lDataRow, 1))) = 0
Application.DisplayAlerts = True
End Sub
It would be more efficient to create an array and write all the data at one time.
Sub SAX()
Dim Data, v
Dim x As Long, y As Long
With ThisWorkbook.Worksheets("Header")
With .Range("A1", .Range("A" & .Rows.Count).End(xlUp))
x = WorksheetFunction.RoundUp(.Cells.Count / 4, 0)
ReDim Data(1 To x, 1 To 4)
x = 1
For Each v In .Cells
If y = 4 Then
x = x + 1
y = 1
Else
y = y + 1
End If
Data(x, y) = v
Next
End With
End With
With ThisWorkbook.Sheets.Add(After:=ThisWorkbook.Sheets(ThisWorkbook.Sheets.Count))
.Name = "Data"
.Range("A1:D1") = Array(1, 2, 3, 4)
.Range("A2:D2").Resize(UBound(Data, 1)).Value = Data
End With
End Sub

Removing ALL Duplicates Row in VBA

I am looking to find out how I can remove ALL duplicate rows (when duplicates exist in the first column) using a VBA macro.
Currently Excel macros delete all duplicate instances EXCEPT for the first instance, which is totally not what I want. I want absolute removal.
A bit shorter solution done for quick morning training:
Sub quicker_Option()
Dim toDel(), i As Long
Dim RNG As Range, Cell As Long
Set RNG = Range("a1:a19") 'set your range here
For Cell = 1 To RNG.Cells.Count
If Application.CountIf(RNG, RNG(Cell)) > 1 Then
ReDim Preserve toDel(i)
toDel(i) = RNG(Cell).Address
i = i + 1
End If
Next
For i = UBound(toDel) To LBound(toDel) Step -1
Range(toDel(i)).EntireRow.Delete
Next i
End Sub
Store the first instance's cell for later deleting.
Then go deleting duplicates until the end.
Dim F as integer, S as integer 'indices for First and Second cells to be compared
Dim Deleted as boolean 'indicates if second line was deleted
Dim First as Range, Second as Range 'First and second cells to be compared
Dim Start as string 'Indicates the position of the first cell's start
Start = "A1" 'can be as you like
Set First = Sheet1.Range(Start) 'Sets the start cell
F = 0 '
Do While First.Value <> "" 'loop while sheet contains data in the column
S = F + 1 'second cell is at least 1 cell below first cell
Deleted = false 'no second cell was deleted yet
Set Second = First.Offset(S,0) 'second cell is an offset of the first cell
Do While Second.Value <> "" 'loop while second cell is in sheet's range with data
if Second.Value = First.Value then 'if values are duplicade
Second.EntreRow.Delete 'delete second cell
Deleted = true 'stores deleted information
else 'if not, second cell index goes next
S = S + 1;
end if
Set Second = First.Offset(S, 0) 'sets second cell again (if deleted, same position, if not deleted, next position
Loop
if Deleted then 'if deleted, should delete first cell as well
First.EntireRow.Delete
else
F = F + 1 'if no duplicates found, first cell goes next
end if
Set First = Sheet1.Range(Start).Offset(F,0) 'sets first cell again (if deleted, same position, if not, next)
Loop
I am using this code to create an Auto reconciliation of general ledger control accounts where if any cell with equal value but opposite sign is cut to sheet 2; hence left with only reconciliation item.
the code:
sub autoRecs()
dim i as long
Application.ScreenUpdating = False
Application.StatusBar = True
Dim i As Long
Cells(5, 6).Select
Dim x As Long
Dim y As Long
x = ActiveCell.Row
y = x + 1
Do Until Cells(x, 6) = 0
Do Until Cells(y, 6) = 0
Application.StatusBar = "Hey Relax! You can rely on me......"
If Cells(x, 6) = Cells(y, 6) * -1 Then
Cells(x, 6).EntireRow.Cut (Worksheets(2).Cells(x, 6).EntireRow)
Cells(y, 6).EntireRow.Cut (Worksheets(2).Cells(y, 6).EntireRow)
Cells(x, 6).Value = "=today()"
Cells(y, 6).Value = "=today()"
Else
y = y + 1
End If
Loop
x = x + 1
y = x + 1
Loop
Application.StatusBar = False
End Sub
Sub deleteBlankCells()`this is to delete unnecessary cells after run the above macro`
Range(Cells(5, 1), Cells(Rows.Count, 1).End(xlUp)).Select
For i = Selection.Rows.Count To 1 Step -1
Application.StatusBar = "OOH! I'm cleaning all the blanks for you....."
If WorksheetFunction.CountA(Selection.Rows(i)) = 0 Then
Selection.Rows(i).EntireRow.Delete
End If
Next i
Application.StatusBar = False
End Sub
I like to work with arrays within VBA, so here is an example.
Assume the data represents the currentregion around A1, but that is easily changed
Read the source data into an array
Check each item in column one to ensure it is unique (countif of that item = 1)
If unique, add the corresponding row number to a Collection
Use the size of th collection and the number of columns to Dim a results array.
Cycle through the collection, writing the corresponding rows to a results array.
Write the results array to the worksheet.
As written, the results are placed to the right of the source data, but could also replace it, or be placed on a different sheet.
Option Explicit
Sub RemoveDuplicatedRows()
Dim vSrc As Variant, vRes() As Variant
Dim rSrc As Range, rRes As Range
Dim colUniqueRows As Collection
Dim I As Long, J As Long
'assume data starts in A1 and represented by currentregion
Set rSrc = Range("a1").CurrentRegion
vSrc = rSrc
Set rRes = rSrc.Offset(0, UBound(vSrc, 2) + 2)
'get collection of non-duplicated rows
Set colUniqueRows = New Collection
For I = 1 To UBound(vSrc)
If WorksheetFunction.CountIf(rSrc.Columns(1), vSrc(I, 1)) = 1 Then _
colUniqueRows.Add I
Next I
'Make up results array
ReDim vRes(1 To colUniqueRows.Count, 1 To UBound(vSrc, 2))
For I = 1 To UBound(vRes, 1)
For J = 1 To UBound(vSrc, 2)
vRes(I, J) = vSrc(colUniqueRows(I), J)
Next J
Next I
rRes.EntireColumn.Clear
rRes.Resize(UBound(vRes)) = vRes
End Sub