I am new to VBA and was trying to write a macro to check duplicates among a column. I have values in columns from A to Z with varying last row number, some may be 5 while some may be 10. Is there any way to check if duplicate value exist among a column and then print "duplicate" on the first row (I dont have any values in the first row for all the columns). I need this for varying last row and last column number.
You can try:
Option Explicit
Public Sub Get_Unique_Count_Paste_Array()
Dim Ob As Object
Dim rng As Range
Dim i As Long
Dim str As String
Dim LR As Long
Dim Item As Variant
With Worksheets("Sheet1")
For i = 1 To 26
Set Ob = CreateObject("scripting.dictionary")
LR = .Cells(.Rows.Count, i).End(xlUp).Row
For Each rng In .Range(Cells(2, i), Cells(LR, i))
str = Trim(rng.Value)
If Len(str) > 0 Then
Ob(str) = Ob(str) + 1
End If
Next rng
For Each Item In Ob.keys
If .Cells(1, i).Value = "" Then
.Cells(1, i).Value = Item
ElseIf .Cells(1, i).Value <> "" Then
.Cells(1, i).Value = .Cells(1, i).Value & ", " & Item
End If
Next Item
Next i
End With
End Sub
Edited Version:
Option Explicit
Public Sub Get_Unique_Count_Paste_Array()
Dim Ob As Object
Dim rng As Range
Dim i As Long
Dim str As String
Dim LR As Long
Dim Item As Variant
With Worksheets("Sheet1")
For i = 1 To 26
Set Ob = CreateObject("scripting.dictionary")
LR = .Cells(.Rows.Count, i).End(xlUp).Row
For Each rng In .Range(Cells(2, i), Cells(LR, i))
str = Trim(rng.Value)
If Len(str) > 0 Then
Ob(str) = Ob(str) + 1
End If
Next rng
For Each Item In Ob.keys
If .Cells(1, i).Value = "" And Ob(Item) > 1 Then
.Cells(1, i).Value = "Duplicate"
Exit For
End If
Next Item
Next i
End With
End Sub
A slight alteration of #error 1004's idea
Private d As Scripting.Dictionary
Private s As String
Function Get_Dupe_Summary(rngInput As Excel.Range) as string
Dim c As Excel.Range
Set d = New Scripting.Dictionary
For Each c In rngInput.Cells
If d.Exists(c.Value) Then
Get_Dupe_Summary = Get_Dupe_Summary & _
IIf(Len(Get_Dupe_Summary) > 0, ",", "") & _
"Dupe : " & c & " on row " & c.Row
Else
d.Add c.Value, 1
End If
Next c
End Function
Related
I have values in column B (green, blue, white....) and I want to count them and the result must appear in column A in the following format (green01, green02, green03...., blue01, blue02, blue03, blue04...., white01, white 02...).
The result must look like in this photo
I have searched the net for a macro, but I didn't find one to fit my needs.
THX
No VBA needed, in A1:
=B1&TEXT(COUNTIF(B$1:B1,B1),"00")
Try the next code, please:
Sub testCountSortColors()
Dim sh As Worksheet, lastRow As Long, i As Long, c As Long
Set sh = ActiveSheet
lastRow = sh.Range("B" & Rows.count).End(xlUp).Row
sh.Range("B1:B" & lastRow).Sort key1:=sh.Range("B1"), order1:=xlAscending, Header:=xlYes
For i = 2 To lastRow
If sh.Range("B" & i).value <> sh.Range("B" & i - 1).value Then
c = 1
Else
c = c + 1
End If
sh.Range("A" & i).value = sh.Range("B" & i).value & Format(c, "00")
sh.Range("A" & i).Font.Color = sh.Range("B" & i).Font.Color
Next
End Sub
I thought you maybe have column headers...
A Unique Count
Adjust the values in the constants section.
Option Explicit
Sub countUnique()
Const SourceColumn As Variant = 2 ' e.g. 2 or "B"
Const TargetColumn As Variant = 1 ' e.g. 1 or "A"
Const FirstRow As Long = 1
Dim rng As Range
Dim dict As Object
Dim Key As Variant
Dim Source As Variant, Target As Variant
Dim i As Long, UB As Long
Dim CurrString As String
Set rng = Columns(SourceColumn).Find(What:="*", _
LookIn:=xlFormulas, SearchDirection:=xlPrevious)
If rng Is Nothing Then GoTo exitProcedure
If rng.Row < FirstRow Then GoTo exitProcedure
Source = Range(Cells(FirstRow, SourceColumn), rng)
Set rng = Nothing
UB = UBound(Source)
Set dict = CreateObject("Scripting.Dictionary")
For i = 1 To UB
If Source(i, 1) <> "" Then
dict(Source(i, 1)) = dict(Source(i, 1)) + 1
End If
Next i
ReDim Target(1 To UB, 1 To 1)
For i = UB To 1 Step -1
CurrString = Source(i, 1)
If CurrString <> "" Then
Target(i, 1) = CurrString & Format(dict(CurrString), "00")
dict(CurrString) = dict(CurrString) - 1
End If
Next i
With Cells(FirstRow, TargetColumn)
.Resize(Rows.Count - FirstRow + 1).ClearContents
.Resize(UB) = Target
End With
MsgBox "Operation finished successfully."
exitProcedure:
End Sub
I want to remove duplicates based on the text in Column I and sum the values in Column C, the data in the other columns doesn't matter.
I do not want a pivot table and I am aware they are the preferred option for this type of thing.
An example of what I'd like to achieve:
I found VBA code and tried to modify it. It doesn't delete all the lines.
Sub Sum_and_Dedupe()
With Worksheets("data")
'deal with the block of data radiating out from A1
With .Cells(1, 1).CurrentRegion
'step off the header and make one column wider
With .Resize(.Rows.Count - 1, .Columns.Count + 1).Offset(1, 0)
.Columns(.Columns.Count).Formula = "=sumifs(c:c, i:i, i2)"
.Columns(3) = .Columns(.Columns.Count).Value
.Columns(.Columns.Count).Delete
End With
'remove duplicates
.RemoveDuplicates Columns:=Array(9), Header:=xlYes
End With
.UsedRange
End With
End Sub
This should be an answer to your question.
However, code might require adaptation if the range in which you look becomes very long.
Option Explicit
Sub test()
Dim wb As Workbook
Dim ws As Worksheet
Dim LastRow As Long, LastCol As Long, a As Double, i As Long
Dim Rng As Range
Dim Cell As Variant, Estimate As Variant
Set wb = ThisWorkbook
Set ws = wb.Sheets(1)
LastRow = ws.Cells(ws.Rows.Count, "I").End(xlUp).Row
LastCol = ws.Cells(1, ws.Columns.Count).End(xlToLeft).Column
Set Rng = ws.Range(ws.Cells(2, 9), ws.Cells(LastRow, 9))
For Each Cell In Rng
i = 0
a = 0
For Each Estimate In Rng
If Estimate.Value = Cell.Value Then
i = i + 1 'Count nr of intances
a = a + ws.Cells(Estimate.Row, 3).Value 'sum booking value
If i > 1 Then
ws.Rows(Estimate.Row).Delete
i = 1
LastRow = LastRow - 1
End If
End If
Next Estimate
ws.Cells(Cell.Row, 3).Value = a 'Enter sum in booked this week
Next Cell
End Sub
You'll either need to change your current sheet name to data, or change the first two lines of this code to fit your needs. sh = the data sheet that you showed us. osh = an output sheet that this code will generate. Note also if column C or I move you can update the positions easily by changing colBooked and colEstimate. If you have more than a thousand unique estimate entries then make the array number larger than 999.
Sub summariseEstimates()
Dim sh As String: sh = "data"
Dim osh As String: osh = "summary"
Dim colBooked As Integer: colBooked = 3
Dim colEstimate As Integer: colEstimate = 9
Dim myArray(999) As String
Dim shCheck As Worksheet
Dim output As Worksheet
Dim lastRow As Long
Dim a As Integer: a = 0
Dim b As Integer
Dim r As Long 'row anchor
Dim i As Integer 'sheets
'Build summary array:
With Worksheets(sh)
lastRow = .Cells.Find("*", searchorder:=xlByRows, searchdirection:=xlPrevious).Row
For r = 2 To lastRow
If r = 2 Then 'first entry
myArray(a) = .Cells(r, colEstimate) & "," & .Cells(r, colBooked)
Else
For b = 0 To a
If VBA.LCase(VBA.Replace(.Cells(r, colEstimate), " ", "")) = VBA.LCase(VBA.Replace(VBA.Split(myArray(b), ",")(0), " ", "")) Then 'match
myArray(b) = VBA.Split(myArray(b), ",")(0) & "," & VBA.Split(myArray(b), ",")(1) + .Cells(r, colBooked)
Exit For
End If
Next b
If b = a + 1 Then 'completed loop = no match, create new array item:
a = a + 1
myArray(a) = .Cells(r, colEstimate) & "," & .Cells(r, colBooked)
End If
End If
Next r
End With
'Create summary sheet:
On Error Resume Next
Set shCheck = Worksheets(osh)
If Err.Number <> 0 Then
On Error GoTo 0
Set output = Worksheets.Add(After:=Worksheets(sh))
output.Name = osh
Err.Clear
Else
On Error GoTo 0
If MsgBox("*" & osh & "* sheet already exists. Proceed to delete and recreate?", vbOKCancel, "Summary") = vbCancel Then
Exit Sub
Else
Application.DisplayAlerts = False
Worksheets(osh).Delete
Set output = Worksheets.Add(After:=Worksheets(sh))
output.Name = osh
End If
End If
'Output to summary sheet:
With Worksheets(osh)
.Cells(1, 1).Value = "ESTIMATE"
.Cells(1, 2).Value = "BOOKED THIS WEEK"
For b = 0 To a
.Cells(b + 2, 1).Value = VBA.Split(myArray(b), ",")(0)
.Cells(b + 2, 2).Value = VBA.Split(myArray(b), ",")(1)
Next b
.Columns("A:B").AutoFit
End With
End Sub
This question already has answers here:
Split comma separated entries to new rows [closed]
(2 answers)
Closed 1 year ago.
I currently have this data in a sheet
Col A Col B Col C
1 A angry birds, gaming
2 B nirvana,rock,band
What I want to do is split the comma separated entries in the third column and insert in new rows like below:
Col A Col B Col C
1 A angry birds
1 A gaming
2 B nirvana
2 B rock
2 B band
I am sure this can be done with VBA but couldn't figure it out myself.
variant using Scripting.Dictionary
Sub ttt()
Dim dic As Object: Set dic = CreateObject("Scripting.Dictionary")
Dim x&, cl As Range, rng As Range, k, s
Set rng = Range([C1], Cells(Rows.Count, "C").End(xlUp))
x = 1 'used as a key for dictionary and as row number for output
For Each cl In rng
For Each s In Split(cl.Value2, ",")
dic.Add x, Cells(cl.Row, "A").Value2 & "|" & _
Cells(cl.Row, "B").Value2 & "|" & LTrim(s)
x = x + 1
Next s, cl
For Each k In dic
Range(Cells(k, "A"), Cells(k, "C")).Value2 = Split(dic(k), "|")
Next k
End Sub
source:
result:
If you have a substantial amount of data, you willfind working with arrays beneficial.
Sub Macro2()
Dim i As Long, j As Long, rws As Long
Dim inp As Variant, outp As Variant
With Worksheets("sheet2")
inp = .Range(.Cells(1, "A"), .Cells(.Rows.Count, "C").End(xlUp)).Value2
For i = LBound(inp, 1) To UBound(inp, 1)
rws = rws + UBound(Split(inp(i, 3), ",")) + 1
Next i
ReDim outp(1 To rws, 1 To 3)
rws = 0
For i = LBound(inp, 1) To UBound(inp, 1)
For j = 0 To UBound(Split(inp(i, 3), ","))
rws = rws + 1
outp(rws, 1) = inp(i, 1)
outp(rws, 2) = inp(i, 2)
outp(rws, 3) = Trim(Split(inp(i, 3), ",")(j))
Next j
Next i
.Cells(1, "A").Resize(UBound(outp, 1), UBound(outp, 2)) = outp
End With
End Sub
This is not a polished solution, but I need to spend some time with the wife.
But still another way of thinking about it.
This code assumes that the sheet is called Sheet4 and the range that needs to be split is col C.
Dim lastrow As Integer
Dim i As Integer
Dim descriptions() As String
With Worksheets("Sheet4")
lastrow = .Range("C1").End(xlDown).Row
For i = lastrow To 2 Step -1
If InStr(1, .Range("C" & i).Value, ",") <> 0 Then
descriptions = Split(.Range("C" & i).Value, ",")
End If
For Each Item In descriptions
.Range("C" & i).Value = Item
.Rows(i).Copy
.Rows(i).Insert
Next Item
.Rows(i).EntireRow.Delete
Next i
End With
This will do what you want.
Option Explicit
Const ANALYSIS_ROW As String = "C"
Const DATA_START_ROW As Long = 1
Sub ReplicateData()
Dim iRow As Long
Dim lastrow As Long
Dim ws As Worksheet
Dim iSplit() As String
Dim iIndex As Long
Dim iSize As Long
'Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
With ThisWorkbook
.Worksheets("Sheet1").Copy After:=.Worksheets("Sheet1")
Set ws = ActiveSheet
End With
With ws
lastrow = .Cells(.Rows.Count, ANALYSIS_ROW).End(xlUp).Row
End With
For iRow = lastrow To DATA_START_ROW Step -1
iSplit = Split(ws.Cells(iRow, ANALYSIS_ROW).Value2, ",")
iSize = UBound(iSplit) - LBound(iSplit) + 1
If iSize = 1 Then GoTo Continue
ws.Rows(iRow).Copy
ws.Rows(iRow).Resize(iSize - 1).Insert
For iIndex = LBound(iSplit) To UBound(iSplit)
ws.Cells(iRow, ANALYSIS_ROW).Offset(iIndex).Value2 = iSplit(iIndex)
Next iIndex
Continue:
Next iRow
Application.CutCopyMode = False
Application.Calculation = xlCalculationAutomatic
'Application.ScreenUpdating = True
End Sub
I have a macro which compares the first 20 characters of strings in two columns, when the customer type is "O" and gives the results. But I need to compare these two columns and if 80% of the strings match, i need to get the result as "ok" else "check". Can someone help me with correcting my code. Thanks
Sub Macro1()
'
'Match Organization names only the first 20 characters
'
'
Dim sht As Worksheet
Dim LR As Long
Dim i As Long
Dim str As String, str1 As String
Set sht = ActiveWorkbook.Worksheets("ORD_CS")
LR = sht.UsedRange.Rows.Count
With sht
For i = 8 To LR
If CStr(.Range("Q" & i).Value) = "O" Then
str = Left(.Range("S" & i).Value, 20)
str1 = Left(.Range("U" & i).Value, 20)
If str = str1 Then
Range("V" & i).Value = "ok"
Else
Range("V" & i).Value = "check"
End If
End If
Next i
End With
End Sub
Maybe use len() and multiply by .8
Sub Button1_Click()
Dim LstRw As Long, Rng As Range, sh As Worksheet, c As Range
Set sh = Sheets("ORD_CS")
With sh
LstRw = .Cells(.Rows.Count, "S").End(xlUp).Row
Set Rng = .Range("S2:S" & LstRw)
For Each c In Rng.Cells
If InStr(1, c.Offset(, 2), Left(c, Len(c) * 0.8)) Then
c.Offset(, 3) = "Yep"
Else: c.Offset(, 3) = "Nope"
End If
Next c
End With
End Sub
Compare column s or t whichever string is smaller.
You can count the string characters to find out which one is smaller.
Sub Button1_Click()
Dim LstRw As Long, Rng As Range, sh As Worksheet, c As Range
Set sh = Sheets("ORD_CS")
With sh
LstRw = .Cells(.Rows.Count, "S").End(xlUp).Row
Set Rng = .Range("S2:S" & LstRw)
For Each c In Rng.Cells
x = IIf(Len(c) < Len(c.Offset(, 1)), 0, 1)
If InStr(1, .Cells(c.Row, "U"), Left(c.Offset(, x), Len(c.Offset(, x)) * 0.8)) Then
.Cells(c.Row, "V") = "Yep"
Else: .Cells(c.Row, "V") = "Nope"
End If
Next c
End With
End Sub
Just keep track of the number of hits and divide that by the total rows you are looking at:
Sub Macro1()
'
'Match Organization names only the first 20 characters
'
'
Dim sht As Worksheet
Dim LR As Long
Dim i As Long
Dim str As String, str1 As String
Dim totalRows as Long, Dim matchRows as Long
Set sht = ActiveWorkbook.Worksheets("ORD_CS")
LR = sht.UsedRange.Rows.Count
totalRows = LR-8
With sht
For i = 8 To LR
If CStr(.Range("Q" & i).Value) = "O" Then
str = Left(.Range("S" & i).Value, 20)
str1 = Left(.Range("U" & i).Value, 20)
If str = str1 Then
Range("V" & i).Value = "ok"
matchRows = matchRows + 1
Else
Range("V" & i).Value = "check"
End If
End If
Next i
End With
'heres ther percentage of hits:
if matchRows/totalRows > .8 Then
msgbox "OK"
else
msgbox "Check"
End if
End Sub
If it's not 80% of the total matching rows you are looking for, but rather comparing to strings to get a number of how aproximately matched they are, you could implement the Levenshtein distance function and do your compare using that. See here for a VBA function that will do that which should be easy to implement in your code
This question already has answers here:
Split comma separated entries to new rows [closed]
(2 answers)
Closed 1 year ago.
I currently have this data in a sheet
Col A Col B Col C
1 A angry birds, gaming
2 B nirvana,rock,band
What I want to do is split the comma separated entries in the third column and insert in new rows like below:
Col A Col B Col C
1 A angry birds
1 A gaming
2 B nirvana
2 B rock
2 B band
I am sure this can be done with VBA but couldn't figure it out myself.
variant using Scripting.Dictionary
Sub ttt()
Dim dic As Object: Set dic = CreateObject("Scripting.Dictionary")
Dim x&, cl As Range, rng As Range, k, s
Set rng = Range([C1], Cells(Rows.Count, "C").End(xlUp))
x = 1 'used as a key for dictionary and as row number for output
For Each cl In rng
For Each s In Split(cl.Value2, ",")
dic.Add x, Cells(cl.Row, "A").Value2 & "|" & _
Cells(cl.Row, "B").Value2 & "|" & LTrim(s)
x = x + 1
Next s, cl
For Each k In dic
Range(Cells(k, "A"), Cells(k, "C")).Value2 = Split(dic(k), "|")
Next k
End Sub
source:
result:
If you have a substantial amount of data, you willfind working with arrays beneficial.
Sub Macro2()
Dim i As Long, j As Long, rws As Long
Dim inp As Variant, outp As Variant
With Worksheets("sheet2")
inp = .Range(.Cells(1, "A"), .Cells(.Rows.Count, "C").End(xlUp)).Value2
For i = LBound(inp, 1) To UBound(inp, 1)
rws = rws + UBound(Split(inp(i, 3), ",")) + 1
Next i
ReDim outp(1 To rws, 1 To 3)
rws = 0
For i = LBound(inp, 1) To UBound(inp, 1)
For j = 0 To UBound(Split(inp(i, 3), ","))
rws = rws + 1
outp(rws, 1) = inp(i, 1)
outp(rws, 2) = inp(i, 2)
outp(rws, 3) = Trim(Split(inp(i, 3), ",")(j))
Next j
Next i
.Cells(1, "A").Resize(UBound(outp, 1), UBound(outp, 2)) = outp
End With
End Sub
This is not a polished solution, but I need to spend some time with the wife.
But still another way of thinking about it.
This code assumes that the sheet is called Sheet4 and the range that needs to be split is col C.
Dim lastrow As Integer
Dim i As Integer
Dim descriptions() As String
With Worksheets("Sheet4")
lastrow = .Range("C1").End(xlDown).Row
For i = lastrow To 2 Step -1
If InStr(1, .Range("C" & i).Value, ",") <> 0 Then
descriptions = Split(.Range("C" & i).Value, ",")
End If
For Each Item In descriptions
.Range("C" & i).Value = Item
.Rows(i).Copy
.Rows(i).Insert
Next Item
.Rows(i).EntireRow.Delete
Next i
End With
This will do what you want.
Option Explicit
Const ANALYSIS_ROW As String = "C"
Const DATA_START_ROW As Long = 1
Sub ReplicateData()
Dim iRow As Long
Dim lastrow As Long
Dim ws As Worksheet
Dim iSplit() As String
Dim iIndex As Long
Dim iSize As Long
'Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
With ThisWorkbook
.Worksheets("Sheet1").Copy After:=.Worksheets("Sheet1")
Set ws = ActiveSheet
End With
With ws
lastrow = .Cells(.Rows.Count, ANALYSIS_ROW).End(xlUp).Row
End With
For iRow = lastrow To DATA_START_ROW Step -1
iSplit = Split(ws.Cells(iRow, ANALYSIS_ROW).Value2, ",")
iSize = UBound(iSplit) - LBound(iSplit) + 1
If iSize = 1 Then GoTo Continue
ws.Rows(iRow).Copy
ws.Rows(iRow).Resize(iSize - 1).Insert
For iIndex = LBound(iSplit) To UBound(iSplit)
ws.Cells(iRow, ANALYSIS_ROW).Offset(iIndex).Value2 = iSplit(iIndex)
Next iIndex
Continue:
Next iRow
Application.CutCopyMode = False
Application.Calculation = xlCalculationAutomatic
'Application.ScreenUpdating = True
End Sub