The code below creates a new set of data on a new sheet but it takes everything that matches the requirements even if it is a duplicate. How can I change the code to eliminate duplicates in the new data set?
Sub Testerss()
Dim c As Range, v As String, arr, x As Long, e
Dim d As Range
Dim ws As Worksheet
Set d = Worksheets("Sheet3").Range("D1")
For Each c In ActiveSheet.Range("D25:D105")
v = Trim(c.Value)
If Len(v) > 0 Then
v = Replace(v, vbLf, " ")
Do While InStr(v, " ") > 0
v = Replace(v, " ", " ")
Loop
arr = Split(v, " ")
For x = LBound(arr) To UBound(arr)
e = arr(x)
If Not IsError(Application.Match(LCase(e), Array("(bye)", "(hello)"), 0)) Then
If x > LBound(arr) Then
d.Value = arr(x - 1) & " " & e
Else
d.Value = "??? " & e
End If
Set d = d.Offset(1, 0)
End If
Next x
End If
Next c
End Sub​
You could add a check to see if the result has already been copied over. First set a range of the results
finalRow = Worksheets("Sheet3").Cells(1000000, 4).end(xlup).row
Set resultRange = Worksheets("Sheet3").Range("D1:D" & finalRow)
Now see if the value you are currently examining is in that range
duplicate = false
for each result in resultRange
if v = result.Value then
duplicate = true
Exit For
end if
next
Now also check for duplicate before proceeding
If Len(v) > 0 and not duplicate then
All together
Set d = Worksheets("Sheet3").Range("D1")
For Each c In ActiveSheet.Range("D25:D105")
finalRow = Worksheets("Sheet3").Cells(1000000, 4).end(xlup).row
Set resultRange = Worksheets("Sheet3").Range("D1:D" & finalRow)
v = Trim(c.Value)
duplicate = false
for each result in resultRange
if v = result.Value then
duplicate = true
Exit For
end if
next
If Len(v) > 0 and not duplicate then
...
Related
At the moment I have some cells that look something like this
What I want to achieve is something that deletes duplicates but also puts all of the green cells into the same row
What I have at the moment is a code like this
Sub Delete_Duplicates()
Worksheets("MySheet").Activate
'Obtain the last row with data on column 2
a = Worksheets("MySheet").Cells(Rows.Count, 2).End(xlUp).Row
'Loop through the name of the items
For b = a To 6 Step -1
CurrentCell = Cells(b, 2).Select
CellValue = Cells(b, 2).Value
CellUp = ActiveCell.Offset(-1, 0)
If ActiveCell.Value = CellUp Then
For c = 8 To 19
If Range(b, c).Interior.Color = RGB(146, 208, 80) Then
Worksheets("MySheet").Range(b, c).Activate
Range(b, c).Copy Destination:=ActiveCell.Offset(-1, 0)
Rows(a).EntireRow.Delete
End If
Next c
End If
Next b
End Sub
What I am hoping that this code does is that it recognises if the value of the active cell is equal to the cell on top and then if their values are equal I loop through the cells from column H to column S and copy the cells that are green and paste them on top
The issue that I have at the moment is that when my code finds two cells with equal names after going to the line
If Range(b, c).Interior.Color = RGB(129, 188, 0) Then
The compiler just skips the rest of the code and wont execute anything else, can anyone help me see why is the rest of my code being skipped?
I m not 100% sure about the code because was to complex but i try to create something:
Sub TEST()
Dim LastRow As Long, i As Long, y As Long, w As Long, k As Long, RowCounter As Long, FirstInstant As Long, o As Long, l As Long
Dim arrNames As Variant, arrNumber(0) As Variant, arrCheck As Variant, arrDelete(0) As Variant, arrColor As Variant, arrSplit As Variant
Dim Found As Boolean, Found_2 As Boolean
RowCounter = 0
FirstInstant = 0
With ThisWorkbook.Worksheets("Sheet2")
LastRow = .Cells(.Rows.Count, "B").End(xlUp).Row
arrNames = .Range("B6:B" & LastRow)
'Loop name
For i = LBound(arrNames) To UBound(arrNames)
'Loop rows
For y = 6 To LastRow
'Check there is a match
If arrNames(i, 1) = .Range("B" & y).Value Then
If FirstInstant = 0 Then
FirstInstant = y
End If
If RowCounter > 0 Then
If arrDelete(0) = "" Then
arrDelete(0) = y & ":" & y
Else
arrSplit = Split(arrDelete(0), ",")
For l = LBound(arrSplit) To UBound(arrSplit)
If arrSplit(l) = y & ":" & y Then
Found_2 = True
Exit For
End If
Next l
If Found_2 = False Then
arrDelete(0) = arrDelete(0) & "," & y & ":" & y
End If
End If
Else
RowCounter = RowCounter + 1
End If
'Loop columns
For w = 3 To 19
'Check if there is color
If .Cells(y, w).Interior.Color = RGB(129, 188, 0) Then
If arrNumber(0) = "" Then
arrNumber(0) = w
Else
arrCheck = Split(arrNumber(0), ",")
Found = False
'Check if the column already excist
For k = LBound(arrCheck) To UBound(arrCheck)
If arrCheck(k) = w Then
Found = True
Exit For
End If
Next k
If Found = False Then
arrNumber(0) = arrNumber(0) & "," & w
End If
End If
End If
Next w
End If
Next y
'Color
If arrNumber(0) <> "" Then
arrColor = Split(arrNumber(0), ",")
For o = LBound(arrColor) To UBound(arrColor)
.Cells(FirstInstant, CLng(arrColor(o))).Interior.Color = RGB(129, 188, 0)
Next o
End If
RowCounter = 0
FirstInstant = 0
Erase arrNumber
Erase arrCheck
Erase arrColor
Next i
.Range(arrDelete(0)).EntireRow.Delete
End With
End Sub
If I have values in cell c3= 2,4,6,8,12,14,18,23,35,78,101,38,30,205,2,101
In This two values are duplicate which is { 2,101 }
I want notification as and when enter any value twice, three-time , forth time, etc in that cell i should come to know which value is repeated. Duplicate values can be shown in adjacent cell D3,
Try this
Sub Test_CheckDups_UDF()
With Range("A1")
.Value = "2,4,6,8,12,14,18,23,35,78,101,38,30,205,2,101"
.Offset(, 1).Value = CheckDups(.Value)
End With
End Sub
Function CheckDups(s As String) As String
Dim a, dic As Object, i As Long
Set dic = CreateObject("scripting.dictionary")
a = Split(s, ",")
For i = LBound(a) To UBound(a)
If dic.Exists(a(i)) = True Then CheckDups = CheckDups & IIf(CheckDups = Empty, "", ",") & a(i) Else dic.Add a(i), 1
Next i
End Function
Here's a code that will highlight the duplicates within the same cell. Tweak it so as to suit your needs
Sub Highlight_Duplicates_Within_Cell()
Dim s, sp, k, c As Range, t As String, f As Boolean, n As Long
For Each c In Range("C3:C13")
c.Font.Color = vbBlack
With CreateObject("Scripting.Dictionary")
.CompareMode = vbTextCompare
sp = Split(c.Value, ",")
For Each s In sp
If Not .Exists(s) Then .Add s, 1 Else .Item(s) = .Item(s) + 1
Next s
For Each k In .Keys
t = "," & k & ","
f = False
n = InStr(1, "," & c.Value & ",", t, vbTextCompare)
Do While n And .Item(k) > 1
If f Then
c.Characters(n, Len(t) - 2).Font.Color = vbRed
End If
n = InStr(n + Len(k), "," & c.Value & ",", t, vbTextCompare)
f = True
Loop
Next k
End With
Next c
End Sub
Try this version too using Regex
Sub Highlight_Duplicates2()
Dim mtch As Object, mtch2 As Object, m As Object, mm As Object, c As Range, txt As String, i As Long
For Each c In Range("C3:C13")
With CreateObject("VBScript.RegExp")
.Global = True
txt = c.Value
.Pattern = " *(\w+)"
Set mtch = .Execute(txt)
For Each m In mtch
.Pattern = "\b" & m.submatches(0) & "\b"
Set mtch2 = .Execute(txt)
If mtch2.Count > 1 Then
For i = 1 To .Execute(txt).Count - 1
Set mm = mtch2(i)
With c.Characters(mm.firstindex + 1, mm.length).Font
.Color = vbRed: .Bold = True
End With
Mid$(txt, mm.firstindex + 1, mm.length) = Space(mm.length)
Next i
End If
Next m
End With
Next c
End Sub
This does not meet your request of trapping duplicates while typing. However to process a comma-separated string (once entered) consider the following user defined function:
Public Function duplist(s As String) As String
Dim s2 As String, arr
Dim kount As Long, i As Long, j As Long
arr = Split(s, ",")
For i = 0 To UBound(arr)
kount = 0
v = arr(i)
For j = 0 To i
If v = arr(j) Then kount = kount + 1
Next j
If kount = 2 Then s2 = s2 & "," & v
Next i
duplist = Mid(s2, 2)
End Function
I have this code that looks at column A and loops through to create an array to paste to another destination, but I want to manipulate it to loop through sets of 250 cells and create a concatenated array and print it to cells B1. After that set of 250, I go cells a251-a501, and so forth until I reach the end of the list and have each set of 250 concatenated ID's (separated by a ";") to print to the next destination row (B1>B2>B3, etc..)
Sub JC_Fill()
Dim varArray() As Variant
Dim x As Long, i As Long
i = 0
x = 1
With ThisWorkbook.Worksheets("Sheet1").UsedRange.Rows.Count
ReDim varArray(1) 'resize array
Do Until Cells(x, 1).Value = ""
If Cells(x, 1) <> "" Then
varArray(i) = Cells(x, 1).Value
i = i + 1
ReDim Preserve varArray(i)
End If
x = x + 1
Loop
ReDim Preserve varArray(i - 1)
End With
ThisWorkbook.Worksheets("Sheet1").Range("B1").Value = varArray
End Sub
How could I edit my Do While/Loop to repeat the process every 250 cells and then concatenate the array to one cell separated by ; and then offset the next batch until I have no more ID's to cycle through?
Try changing your code this way:
Sub JC_Fill()
Dim OutString
Dim x As Long, i As Long
Dim out_row As Long
i = 0
x = 1
out_row = 1
With ThisWorkbook.Worksheets("Sheet1").UsedRange.Rows.Count
OutString = ""
Do Until Cells(x, 1).Value = ""
If Cells(x, 1) <> "" Then
If (x > 1) Then OutString = OutString & ";"
OutString = OutString & Cells(x, 1).Value
End If
If (x Mod 250) = 0 Then
ThisWorkbook.Worksheets("Sheet1").Range("B" & out_row).Value = OutString
OutString = ""
out_row = out_row + 1
End If
x = x + 1
Loop
End With
ThisWorkbook.Worksheets("Sheet1").Range("B" & out_row).Value = OutString
End Sub
For interest, you can do this without looping each of the 250 cells.
Sub x()
Dim n As Long, v As Variant, r As Range, n2 As Long
n = 5 '250 for you
n2 = Range("A" & Rows.Count).End(xlUp).Row
Set r = Range("A1").Resize(n)
Do While Len(r(1)) > 0
If n2 - r(1).Row < n Then Set r = r.Resize(n2 - r(1).Row + 1)
If r.Count = 1 Then
v = r.Value
Else
v = Join(Application.Transpose(r), ";")
End If
Range("B" & Rows.Count).End(xlUp)(2).Value = v
Set r = r.Offset(n)
Loop
End Sub
The code below extracts & format values from the range B6:E6, and then stores them in the variable. Afterwards, the routine sorts the collection of 4 variables in the ascending order. When sorted they're being put into the range L31:O31.
The problem is that if there are less than 4 variables selected, say 3, the routine will skip L31 cell, and put the rest to M31:O31. Whilst it should be input as L31:N31, and O31 - blank.
How can the code be modified to make it fulfill the data starting from L31 if less than 4 variables are in the collection?
Function ExtractKey(s As Variant) As Long
Dim v As Variant, n As Long
v = Trim(s) 'remove spaces leave only spaces between words
If v Like "*(*)" Then 'if it's SOPXX (YYYY) then
n = Len(v) 'find number of the characters
If n = 11 Then
v = Mid(v, n - 7, 7) 'find the number of SOP + year in bracket
ElseIf n = 12 Then
v = Mid(v, n - 8, 8)
End If
v = Replace(v, "(", "") 'replace the brackets with nothing
v = Replace(v, " ", "")
'SOP10 (2015) doesn't have to go first before SOP12 (2014); switch figures
If n = 11 Then
v = Right(v, 4) + Left(v, 1)
ElseIf n = 12 Then
v = Right(v, 4) + Left(v, 2)
End If
ExtractKey = CLng(v)
Else
ExtractKey = 0
End If
End Function
Sub Worksheet_Delta_Update()
Dim SourceRange As Range, TargetRange As Range
Dim i As Long, j As Long, minKey As Long, minAt As Long
Dim v As Variant
Dim C As New Collection
Set SourceRange = Worksheets("t").Range("B6:E6")
Set TargetRange = Worksheets("x").Range("L31:O31")
For i = 1 To 4
v = SourceRange.Cells(1, i).Value
C.Add Array(ExtractKey(v), v)
Next i
'transfer data
For i = 1 To 4
minAt = -1
For j = 1 To C.Count
If minAt = -1 Or C(j)(0) < minKey Then
minKey = C(j)(0)
minAt = j
End If
Next j
TargetRange.Cells(1, i).Value = C(minAt)(1)
C.Remove minAt
Next i
End Sub
You could add one variable e.g. col which will be used instead of variable i when the value is inserted into TargetRange. This variable will work the same way as the i works but it will be incremented only when the value which is inserted is not empty. HTH
'transfer data
Dim col As Integer
col = 1
For i = 1 To 4
minAt = -1
For j = 1 To C.Count
If minAt = -1 Or C(j)(0) < minKey Then
minKey = C(j)(0)
minAt = j
End If
Next j
If (C(minAt)(1) <> "") Then
TargetRange.Cells(1, col).Value = C(minAt)(1)
col = col + 1
End If
C.Remove minAt
Next i
I am running sub where it compares two cells (B and D/or string Received) from one sheet ("DATA") with two cells (C, H) from another sheet ("Incoming_report"), and if they match it transposes I, G cells from Incoming to Data.
It is done by combining two cells from Incoming_report sheet and writing new value in Z column for example "123456" from C and H to f.e. "123456Received" (there another 5 statuses (Received, Rejected, Sent...., but I need the ones only that was Received)
Then I am taking from Data Sheet B column for example 123456 and only Received (there might be another 5 statuses, but I only need the one that was received).
That makes all sence to me and works pretty good, but I have to work with more than 500k rows in each sheet. What happens - 500,000 times two cells are combined and searched in Z column in another sheet among another 500,000 for possible match, if nothing found then N/A, and then 2 combination, 3rd, 4th... till 500,000. I added the Display status bar and I see how slowly it goes (only 900 rows per minute, so for one minor mapping it would take more than 10 hours). Here is the sub itself, can anyone share ideas how to improve it to make it work faster? Thanks a million.
Sub incoming_fetch()
Application.ScreenUpdating = False
Dim incr As Long
Dim x As String
n = Sheets("Data").Range("A" & Rows.Count).End(xlUp).Row
Z = Sheets("Incoming_report").Range("D" & Rows.Count).End(xlUp).Row
For i2 = 2 To Z
Sheets("Incoming_report").Range("Z" & i2).Value = Sheets("Incoming_report").Range("C" & i2).Value & Sheets("Incoming_report").Range("H" & i2).Value
Next i2
For i = 3 To n
Application.DisplayStatusBar = True
Application.StatusBar = i
x = Sheets("Data").Range("B" & i).Value & "Received"
If Sheets("Incoming_report").Range("Z:Z").Find(x, lookat:=xlWhole, LookIn:=xlFormulas) Is Nothing Then
Sheets("Data").Range("L" & i) = "N/A"
Sheets("Data").Range("M" & i) = "N/A"
Else
incr = Sheets("Incoming_report").Range("Z:Z").Find(x, lookat:=xlWhole, LookIn:=xlFormulas).Row
Sheets("DATA").Range("L" & i) = Sheets("Incoming_report").Range("I" & incr)
Sheets("DATA").Range("M" & i) = Sheets("Incoming_report").Range("G" & incr)
End If
Next i
End Sub
EDIT2: fixed source columns :
Sub incoming_fetch()
Dim i As Long, n As Long, z As Long, num As Long
Dim x As String
Dim shtIn As Worksheet, shtData As Worksheet
Dim dict As Object, arrC, arrH, arrG, arrI, v, arr, r1, r2
Dim t
Set dict = CreateObject("scripting.dictionary")
Set shtIn = Sheets("Incoming_report")
Set shtData = Sheets("Data")
n = shtData.Range("A" & Rows.Count).End(xlUp).Row
z = shtIn.Range("D" & Rows.Count).End(xlUp).Row
t = Timer
'get all values from Cols C, H, L, M
arrC = shtIn.Range(shtIn.Range("C2"), shtIn.Range("C" & z)).Value
arrH = shtIn.Range(shtIn.Range("H2"), shtIn.Range("H" & z)).Value
arrG = shtIn.Range(shtIn.Range("G2"), shtIn.Range("G" & z)).Value
arrI = shtIn.Range(shtIn.Range("I2"), shtIn.Range("I" & z)).Value
Debug.Print "Get Arrays: " & Timer - t
t = Timer
'create a lookup dictionary of all the ColC values
' (where ColH = "Received")
num = UBound(arrC, 1)
For i = 1 To num
v = arrC(i, 1)
If arrH(i, 1) = "Received" And Len(v) > 0 Then
dict(v) = Array(arrI(i, 1), arrG(i, 1))
End If
Next i
'free up some memory
Erase arrC: Erase arrH: Erase arrI: Erase arrG
Debug.Print "Filled dict: " & Timer - t
t = Timer
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
On Error GoTo haveError
For i = 3 To n
If i Mod 500 = 0 Then Application.StatusBar = i
x = shtData.Range("B" & i).Value
If dict.exists(x) Then
arr = dict(x)
r1 = arr(0)
r2 = arr(1)
Else
r1 = "N/A": r2 = "N/A"
End If
With shtData
.Range("L" & i) = r1
.Range("M" & i) = r2
End With
Next i
Debug.Print "Done: " & Timer - t
haveError:
Application.ScreenUpdating = True
Application.Calculation = xlCalculationAutomatic
End Sub