Getting unique values for multiple column separatly - excel

What is worng with my function its loading the two different column A and B and pasting the unique values of column A into Column M and N.
I want to repeat this function for the 7 columns.
I would appreciate your help in this regards.
Sub GetUniques()
Dim d As Object, c As Variant, i As Long, lr As Long, lr2 As Long, lr3 As Long, lr4 As Long, lr5 As Long, lr6 As Long
Set d = CreateObject("Scripting.Dictionary")
lr = Cells(Rows.Count, 1).End(xlUp).Row
c = Range("A2:A" & lr)
lr2 = Cells(Rows.Count, 2).End(xlUp).Row
e = Range("B2:B" & lr2)
For i = 1 To UBound(c, 1)
d(c(i, 1)) = 1
Next i
For i = 1 To UBound(e, 1)
d(e(i, 1)) = 1
Next i
Range("M2").Resize(d.Count) = Application.Transpose(d.keys)
Range("N2").Resize(d.Count) = Application.Transpose(d.keys)
End Sub

It looks like your plan is to have a lr variable for each column as well as loops and transpose statements. You can avoid this by nesting your code in a column loop.
The current Column range is hard coded here (A to E) but this can be updated to be dynamic as needed. The output is also hard coded to be dropped 9 columns to the right of the input column. This aligns with A to J, B to K, etc.
Sub GetUniques()
Dim c As Variant, i As Long, lr As Long, col As Long
Dim d As Object
For col = 1 To 5 'Column A to E
Set d = CreateObject("Scripting.Dictionary")
lr = Cells(Rows.Count, col).End(xlUp).Row
c = Range(Cells(2, col), Cells(lr, col))
For i = 1 To UBound(c, 1)
d(c(i, 1)) = 1
Next i
Cells(2, col + 9).Resize(d.Count) = Application.Transpose(d.keys)
Set d = Nothing
Next col
End Sub

I am adding the UNIQUE- solution - for completeness:
You can either use a manual formula in J2: =UNIQUE(A:E,TRUE) - the second parameter tells UNIQUE to put out unique values per column --> it will spill from J to N.
You can use this formula in a VBA-routine as well:
Public Sub writeUniqueValues(rgSource As Range, rgTargetTopLeftCell As Range)
With rgTargetTopLeftCell
.Formula2 = "=UNIQUE(" & rgSource.Address & ",TRUE)"
With .SpillingToRange
.Value = .Value 'this will replace the formula by values
End With
End With
End Sub
You can then use this sub like this:
Public Sub test_writeUniqueValues()
With ActiveSheet 'be careful: you should always use explicit referencing
Dim lr As Long
lr = .Cells(Rows.Count, 1).End(xlUp).Row
writeUniqueValues .Range("A2:E" & lr), .Range("J2")
End With
End Sub
It would be interesting to compare performance of both solutions (using the formula vs. using a dictionary) ...

Related

Hod to copy data from one sheet to another with limited selection of columns using VBA

I need to copy data from one sheet to another with a limited selection of columns using VBA, not continuous and transpose the copied data in a column while pasting to another sheet. Also, I want to skip the empty cells while doing so.
I want to apply a loop but I am not able to declare the ranges of cells exactly as they should be. I am very new to VBA and below is the code which I am using trying to achieve the goal.
Option Explicit
Sub CopyPasteLoop()
Dim X As Long
Dim Y As Long
Dim Col As Long
Dim row1 As Long
'Dim A As Long
Col = 1
Sheets("Copy").Activate
'For A = 1 To 10000
row1 = Sheets("Copy").Range(.Cells(.Rows.Count, Col)).End(xlUp).row
Sheets("Key Entry Data").Activate
X = Sheet2.Range("A" & Rows.Count).End(xlUp).row
'Y = Sheet1.Cells(1, Columns.Count).End(xlToLeft).Column
Sheets("Copy").Activate
Sheet1.Range("Col" & 2, "Col" & row1).Select
Selection.Copy
'X = X + 1
Sheets("Key Entry Data").Activate
Sheet2.Cells(X).Select
Sheet2.Range("A" & X).PasteSpecial xlPasteValues
Col = ActiveCell.Next.EntireColumn.Cells(1).Select
'Next X
End Sub
In general you should avoid using .Acitvate and .Select as described here. In your code you can completely leave out those parts. This and the unqualified ranges (as mentioned in the comments) are most likely the cause of your problems. Here is your corrected code:
Option Explicit
Sub CopyPasteLoop()
Dim X As Long
Dim Y As Long
Dim Col As Long
Dim row1 As Long
'Dim A As Long
Col = 1
'For A = 1 To 10000
row1 = Sheets("Copy").Cells(Rows.Count, Col).End(xlUp).Row
X = Sheet2.Range("A" & Rows.Count).End(xlUp).Row
'Y = Sheet1.Cells(1, Columns.Count).End(xlToLeft).Column
Sheet1.Range(Sheet1.Cells(2, Col), Sheet1.Cells(row1, Col)).Copy Sheet2.Range("A" & X)
'X = X + 1
'Next X
End Sub
Please note that the For loop you commented out will not work. As it's unclear from your question what that loop is supposed to achieve I'm not able to correct it to what exactly you're trying to do. In general, you don't need X = X + 1 inside a For loop (it will skip every second integer this way), as the For ... To statement takes care of that.
Thank you for sharing this M.Schalk, I am using the below code to copy the data to another sheet. Can you look at the code and share an effective one with me?
Option Explicit
Sub EmailIDCopy()
Dim X As Long 'X is the value of Row
Dim Y As Long
Dim Col As Long 'Col is used to column of Sheet2
Dim row1 As Long 'row1 is currently being used for defining the last row of column
Dim M As Long
Col = 1
Sheets("Copy").Activate
For M = 1 To 5
row1 = Sheets("Copy").Cells(Rows.Count, Col).End(xlUp).row
Sheets("Key Entry Data").Activate
X = Sheet2.Range("A" & Rows.Count).End(xlUp).row
Sheets("Copy").Activate
Sheet1.Range(Cells(2, Col), Cells(row1, Col)).SpecialCells(xlCellTypeVisible).SpecialCells(xlCellTypeConstants).Copy
X = X + 1
Sheets("Key Entry Data").Activate
Sheet2.Cells(X).Select
Sheet2.Range("A" & X).PasteSpecial xlPasteValues
Col = Col + 2
Next M
End Sub
thank you.

Paste from list not found in current range to bottom of current range

I have column A that has all existing categories, new categories are listed in column C. I'm trying to determine how to take these new categories, and add them to column "a" if they aren't already in column A. In the example the new categories in column C are added to column A even if there are already in column A. I would also need range("a1") in the if-then line to be a dynamic range since new categories will be added as the code runs. Some constructive criticism would be greatly appreciated as well to help me in the future.
Sub newcategory()
Dim newcatcount As Integer
Dim i As Integer
newcat = Range("c100000").End(xlUp).Row
For i = 1 To newcat
If Cells(i, 3).Value <> Range("a1") Then
Cells(i, 3).Select
Selection.copy
Range("a100000").End(xlUp).Offset(1, 0).Select
ActiveSheet.Paste
End If
Next
End Sub
Please give this a try...
Sub AddNewCategories()
Dim lrA As Long, lrC As Long, i As Long, j As Long
Dim x, y, z(), dict
lrA = Cells(Rows.Count, 1).End(xlUp).Row
lrC = Cells(Rows.Count, 3).End(xlUp).Row
'Array to hold the categories in column A starting from Row1, assuming the categories start from A1. If not, change it accordingly.
x = Range("A1:A" & lrA).Value
'Array to hold the new categories in column C starting from Row1, assuming the categories start from C1. If not, change it accordingly.
y = Range("C1:C" & lrC).Value
Set dict = CreateObject("Scripting.Dictionary")
For i = 1 To UBound(x, 1)
dict.Item(x(i, 1)) = ""
Next i
For i = 1 To UBound(y, 1)
If Not dict.exists(y(i, 1)) Then
dict.Item(y(i, 1)) = ""
j = j + 1
ReDim Preserve z(1 To j)
z(j) = y(i, 1)
End If
Next i
If j > 0 Then
Range("A" & lrA + 1).Resize(j).Value = Application.Transpose(z)
End If
Set dict = Nothing
End Sub
you could use excel built in RemoveDuplicates() function, as follows (mind the comments):
Option Explicit
Sub newcategory()
Dim newcat As Range
With Worksheets("Categories") ' change "Categories" to your actual sheeet name
Set newcat = .Range("C1", .Cells(.Rows.Count, 3).End(xlUp)) ' get the range of all nwe categories in reference sheet column C from row 1 down to last not empty one
.Cells(.Rows.Count, 1).End(xlUp).Resize(newcat.Rows.Count).Value = newcat.Value ' append new categories values below existing categories in column A
.Range("A1", .Cells(.Rows.Count, 1).End(xlUp)).RemoveDuplicates Columns:=Array(1), Header:=xlNo ' remove duplicates
End With
End Sub

Creat VBA Dictionary Entry When Criteria Is Met

I am new to VBA Dictionaries. What I am trying to do is evaluate each row of a sheet. If the value in Column J is "100", then I want to create a key/item entry into the dictionary. If any other value is in Column J, I do not want the entry to be created and for the macro to look at the next row of data.
I currently have the code below:
Dim x, x2, y, y2()
Dim i As Long
Dim dict As Object
Dim LastRowTwo As Long, shtOrders As Worksheet, shtReport As Worksheet
Set shtOrders = Worksheets("Orders")
Set shtReport = Worksheets("Mapping")
Set dict = CreateObject("Scripting.Dictionary")
With shtReport
LastRow = .Range("G" & Rows.Count).End(xlUp).Row
x = .Range("G2:G" & LastRow).Value
x2 = .Range("I2:I" & LastRow).Value
Set SelectionRNG = Worksheets("Mapping").Range("G2:J" & LastRow)
For Each rngrow In SelectionRNG.Rows
If rngrow.Cells(1, 4) = "100" Then
dict.Item(x(i, 1)) = x2(i, 1)
End If
Next
End With
I know that this line:
dict.Item(x(i, 1)) = x2(i, 1)
Is where my problem is. I have used that syntax in the code below and it works fine:
For i = 1 To UBound(x, 1)
dict.Item(x(i, 1)) = x2(i, 1)
Next i
I am stuck on how to change the syntax of either my "rngrow code" to add a dictionary entry without using the "i" or to adjust the code directly above to include an IF statement to check the value in column J (and only create an entry if J = 100 for the row currently being assessed.
If the code above was not clear, the key would be in Column G and the item would be in column I. (I later lookup Column G and retrieve column I's value.)
As I said, I am new to this, so I appreciate any help!
Cheers!
I would get rid of rngrow and just loop through rows 2 to LastRow:
Dim i As Long
Dim dict As Object
Dim LastRow As Long, shtReport As Worksheet
Set shtReport = Worksheets("Mapping")
Set dict = CreateObject("Scripting.Dictionary")
With shtReport
LastRow = .Range("G" & .Rows.Count).End(xlUp).Row
For i = 2 To LastRow
If .Cells(i, "J") = "100" Then
dict.Item(.Cells(i, "G").Value) = .Cells(i, "I").Value
End If
Next
End With

Remove duplicates from column A based on existing values in column B using VBA

I need to input data in column A and column B and get the data that's in column A but not in column B written to column C.
Examples of what I need:
A slightly different and faster approach without looping through cells on the sheet would be this...
Private Sub CommandButton1_Click()
Dim x, y(), dict
Dim i As Long, j As Long
x = Range("A1").CurrentRegion
Set dict = CreateObject("Scripting.Dictionary")
Columns("C").ClearContents
For i = 1 To UBound(x, 1)
dict.Item(x(i, 2)) = ""
Next i
j = 1
For i = 1 To UBound(x, 1)
If Not dict.exists(x(i, 1)) Then
ReDim Preserve y(1 To j)
y(j) = x(i, 1)
j = j + 1
End If
Next i
Range("C1").Resize(UBound(y), 1) = Application.Transpose(y)
End Sub
Place this in the code file behind your sheet and change CommandButton1 to the name of your button.
Option Explicit
Private Sub CommandButton1_Click()
Dim r As Range, matched_ As Variant, counter_ As Long
'Loop in each cell in Column A
For Each r In Range("A1:A" & Cells(Rows.Count, 1).End(xlUp).Row)
If Not IsEmpty(r) Then
'Loop for a matching value in Column B
matched_ = Application.Match(r.Value, Columns(2), 0)
'If match not found, write the value in Column C
If IsError(matched_) Then
counter_ = counter_ + 1
Range("C" & counter_) = r.Value
End If
End If
Next r
End Sub

Remove duplicate values within dynamic ranges identified by text strings

Text “endofdata” in col B identifies the boundaries of multiple ranges on a single sheet. I’m trying to step through each range and remove duplicate values in columns E and F within each range. I also call a routine that deletes blank rows that are generated when duplicates are removed. The bottom row with “endofdata” is always removed when .removeduplicates is executed.
I’ve tried the Do loop but it’s failing. (It works for the first range but fails for the next range) Please suggest how to make this work. What kind of loop should I use? How should I search for “endofdata” string? Thank you very much in advance.
Sub RemoveDupsinRange()
Dim LastRow As Long, i As Long, startRow, EndRow
Call setSheets
LastRow = wsQC.Cells(wsQC.Rows.Count, "A").End(xlUp).Row
Debug.Print LastRow
For i = LastRow To 1 Step -1
Do
If wsQC.Cells(i, 2).Value = "endofdata" Then
startRow = i
End If
i = i - 1
Loop Until wsQC.Cells(i, 2).Value = "endofdata"
EndRow = i
i = i - 1
Range(startRow & ":" & EndRow).Select
Selection.removeduplicates Columns:=Array(5, 6), _
Header:=xlNo
Call DeleteBlanks
Next i
End Sub
I just tested this loop and it worked.
Sub RemoveDupsinRange()
Dim LastRow As Long, i As Long, rStart As Range, rEnd As Range
Call setSheets
LastRow = wsQC.Cells(wsQC.Rows.Count, "A").End(xlUp).Row
Debug.Print LastRow
Set rEnd = wsQC.Cells(LastRow, 2)
For i = LastRow To 2 Step -1
Do
i = i - 1
If wsQC.Cells(i, 2).Value = "endofdata" Then
Set rStart = wsQC.Cells(i, 2)
End If
Loop Until wsQC.Cells(i, 2).Value = "endofdata"
wsQC.Range(rStart.Offset(, -1), rEnd.Offset(, 4)).RemoveDuplicates Columns:=Array(5, 6), Header:=xlNo
Set rEnd = rStart
Call DeleteBlanks
Next i
End Sub

Resources