In my Column D i have numbers. Normally most of the rows for Col D will have single number eg 2 or 12 or 22 and so on. However on some occasion it will have two or more numbers eg 2, 4,12. The numbers in the cell are separated by commas.
Example:
Col D
Row1 1
Row2 4
Row3 2,12
Row4 11,1
Row5 2,1
Row6 3
Row7 21
Row8 1,11,15
Row9 10,1,9
Row10 1,16
How can I select
all the rows that has in Col D the number 1
ie in the above example it would select row1,row4,row5,row8,row9 and row10
Many thks
This will do it:
Sub SelectRows()
Dim lastRow As Long
Dim rng As Range, rw As Range
With ThisWorkbook.Sheets("test") ' change to your specific sheetname
lastRow = .Cells.Find(What:="*", _
SearchOrder:=xlRows, _
SearchDirection:=xlPrevious, _
LookIn:=xlValues).Row
For Each rw In .Columns("D").Rows
If InStr(1, rw.Value, "1") > 0 Then
If rng Is Nothing Then
Set rng = rw
Else
Set rng = Union(rng, rw)
End If
End If
If rw.Row = lastRow Then Exit For
Next rw
End With
rng.Select
End Sub
Edit #1
This improved code has no false positives as the earlier version:
Sub SelectRows()
Dim addBool As Boolean
Dim lastRow As Long, lenTempStr As Long
Dim rng As Range, rw As Range
Dim tempStr As Variant
With ThisWorkbook.Sheets("test") ' change to your specific sheetname
lastRow = .Cells.Find(What:="*", _
SearchOrder:=xlRows, _
SearchDirection:=xlPrevious, _
LookIn:=xlValues).Row
For Each rw In .Columns("D").Rows
addBool = False
tempStr = Replace(rw.Value, Chr(32), vbNullString)
lenTempStr = Len(tempStr)
If InStr(1, tempStr, "1") > 0 Then
If lenTempStr = 1 Then
addBool = True
ElseIf InStr(1, tempStr, ",1,") > 0 Then
addBool = True
ElseIf Left(tempStr, 2) = "1," Then
addBool = True
ElseIf Right(tempStr, 2) = ",1" Then
addBool = True
End If
If addBool Then
If rng Is Nothing Then
Set rng = rw
Else
Set rng = Union(rng, rw)
End If
End If
End If
If rw.Row = lastRow Then Exit For
Next rw
End With
rng.Select
End Sub
Sub Maybe_A()
Dim c As Range, i As Long, a As String
a = ""
For Each c In Range("D2:D" & Cells(Rows.Count, 4).End(xlUp).Row)
For i = LBound(Split(c, ",")) To UBound(Split(c, ","))
If Trim(Split(c, ",")(i)) = "1" Then
If a = "" Then
a = c.Address(0, 0)
Else
a = a & "," & c.Address
End If
Exit For
End If
Next i
Next c
Range(a).EntireRow.Select
End Sub
Many thks to all of you. Much appreciated. Below is the code which I came up with your help. Not sure how good it is and if it would do, although it seems to working. I had not seen select case true being used before!
Sub FindN()
Dim rC As Range, Rng As Range
Dim sFind As String, fC1 As String, fC2 As String, fC3 As String, fC4 As String
sFind = [d3].Value 'type the numeral to find in cell D3
'find criteria - i think there are four outcomes
fC1 = sFind: fC2 = sFind & ",*": fC3 = "*," & sFind & ",*": fC4 = "*," & sFind
Rng = Range("d4:d14")
Cells.EntireRow.Hidden = False
For Each rC In Rng
Select Case True
Case rC.Value = Val(fC1) 'for numeric 1
rC.EntireRow.Hidden = False
Case rC.Value Like fC2
rC.EntireRow.Hidden = False
Case rC.Value Like fC3
rC.EntireRow.Hidden = False
Case rC.Value Like fC4
rC.EntireRow.Hidden = False
Case Else
rC.EntireRow.Hidden = True
End Select
Next
End Sub
Related
I have this problem I can't seem to fix...
In column L I have certain roles, these roles are divided by || (pipes).
The problem: Some people deliver these roles they want to use like this:
Testing||Admin||Moderator||
But this doesn't work for the script we use to import these roles, what I would like to see is that whenever || (pipes) are used and after the pipes are used if there isn't any text following it up it should delete the pipes at the end.
What I tried is the find and replace option, but this also removes the pipes in between the text.
Hope someone can help me!
Problem:
Testing||Admin||Moderator||
Solution:
Testing||Admin||Moderator
A simple formula can solve your requirements
=IF(RIGHT(TRIM(A1),2)="||",LEFT(TRIM(A1),LEN(TRIM(A1))-2),A1)
The above formula is based on the below logic.
Check if the right 2 characters are ||
If "Yes", then take the left characters (LEN - 2)
If "No", then return the string as it is.
If you still want VBA then try this code which will make the change in the entire column in one go. Explanation about this method is given HERE.
For demonstration purpose, I am assuming that the data is in column A of Sheet1. Change as applicable.
Option Explicit
Sub Sample()
Dim ws As Worksheet
Dim lrow As Long
Dim rng As Range
Dim sAddr As String
Set ws = Sheet1
With ws
lrow = .Range("A" & .Rows.Count).End(xlUp).Row
Set rng = .Range("A1:A" & lrow)
sAddr = rng.Address
rng = Evaluate("index(IF(RIGHT(TRIM(" & sAddr & _
"),2)=""||"",LEFT(TRIM(" & sAddr & _
"),LEN(TRIM(" & sAddr & _
"))-2)," & sAddr & _
"),)")
End With
End Sub
In Action:
I only changed the name of the worksheet and the range to L and L2:L. – Ulquiorra Schiffer 17 mins ago
There are different ways of doing this, but here is one:
Function FixPipes(val As String) As String
Dim v As Variant
v = Split(val, "||")
If Len(v(UBound(v))) > 0 Then
FixPipes = val
Else
FixPipes = Mid$(val, 1, Len(val) - 2)
End If
End Function
Here's another way to do it:
Function FixPipes(val As String) As String
If Mid$(val, Len(val) - 1, 2) <> "||" Then
FixPipes = val
Else
FixPipes = Mid$(val, 1, Len(val) - 2)
End If
End Function
Usage:
Sub test()
Debug.Print FixPipes("Testing||Admin||Moderator||")
End Sub
Or:
Sub LoopIt()
' remove this line after verifying the sheet name
MsgBox ActiveSheet.Name
Dim lIndex As Long
Dim lastRow As Long
lastRow = Range("L" & Rows.Count).End(xlUp).Row
For lIndex = 1 To lastRow
Range("L" & lIndex) = FixPipes(Range("L" & lIndex))
Next
End Sub
https://learn.microsoft.com/en-us/office/vba/language/reference/user-interface-help/split-function
https://learn.microsoft.com/en-us/office/vba/language/reference/user-interface-help/mid-function
A tiny alternative using negative filtering would be:
Function FixPipes(ByVal s As String, Optional delim As String = "||") As String
Dim tmp: tmp = Filter(Split(s & "$$", delim), "$$", False)
FixPipes = Replace(Join(tmp, delim), "$$", "")
End Function
=IF(UNICODE(RIGHT(A2,1))+UNICODE(LEFT(RIGHT(A2,2),1))=248,LEFT(A2,LEN(A2)-2),A2)
Solution using the Replace formula, if you want to do this in VBA you can use the replace function in VBA as well
Remove Trailing String
All Except OP
This is a follow-up question on How to create data validation based on multiple roles?
.
The answers from Siddharth Rout and braX are valid for someone who might stumble upon this post.
They would have to be adjusted for OP because this case is 'clouded' by an already existing Worksheet_Change event.
OP (Ulquiorra)
To not complicate I have integrated a code snippet, which uses a function (similar to the posted solutions), into your existing code and removed Areas since it seems redundant after seeing what you are trying to accomplish.
The Snippet
Application.ScreenUpdating = False
Application.EnableEvents = False
Dim cel As Range
For Each cel In rng.Cells
cel.Value = removeTrail(cel.Value, Delimiter)
Next cel
Application.EnableEvents = True
The Function
Function removeTrail( _
ByVal SearchString As String, _
ByVal RemoveString As String, _
Optional ByVal doTrim As Boolean = True) _
As String
If doTrim Then
removeTrail = Trim(SearchString)
Else
removeTrail = SearchString
End If
If Right(removeTrail, Len(RemoveString)) = RemoveString Then
removeTrail = Left(removeTrail, Len(removeTrail) - Len(RemoveString))
End If
End Function
The Worksheet Change (modified)
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Range)
Const RolesList As String = "Admin,Clerk,Moderator,User"
Const FirstCellAddress As String = "L2"
Const Delimiter As String = "||"
Dim rng As Range
With Range(FirstCellAddress)
Set rng = Intersect(.Resize(.Worksheet.Rows.Count - .Row + 1), Target)
End With
If rng Is Nothing Then
Exit Sub
End If
' The Snippet
Application.ScreenUpdating = False
Application.EnableEvents = False
Dim cel As Range
For Each cel In rng.Cells
cel.Value = removeTrail(cel.Value, Delimiter)
Next cel
Application.EnableEvents = True
Dim Roles() As String: Roles = Split(RolesList, ",")
Dim dRng As Range
Dim aRng As Range
Dim Curr() As String
Dim cMatch As Variant
Dim n As Long
Dim isFound As Boolean
For Each cel In rng.Cells
If Not IsError(cel) Then
Curr = Split(cel.Value, Delimiter)
For n = 0 To UBound(Curr)
cMatch = Application.Match(Curr(n), Roles, 0)
If IsError(cMatch) Then
isFound = True
Exit For
Else
' Remove this block if you don't need case-sensitivity.
If StrComp(Curr(n), Roles(cMatch - 1), _
vbBinaryCompare) <> 0 Then
isFound = True
Exit For
End If
End If
Next n
If isFound Then
isFound = False
If dRng Is Nothing Then
Set dRng = cel
Else
Set dRng = Union(dRng, cel)
End If
End If
End If
Next cel
rng.Interior.Color = xlNone
If Not dRng Is Nothing Then
dRng.Interior.Color = vbRed
End If
Application.ScreenUpdating = True
End Sub
This is the piece of code I have in the worksheet change event (also module1) and also in the same active worksheet as module2:
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Range)
Const RolesList As String = "Testing"
Const FirstCellAddress As String = "L2"
Const Delimiter As String = "||"
Dim rng As Range
With Range(FirstCellAddress)
Set rng = Intersect(.Resize(.Worksheet.rows.Count - .Row + 1), Target)
End With
If rng Is Nothing Then
Exit Sub
End If
Dim Roles() As String: Roles = Split(RolesList, ",")
Dim dRng As Range
Dim aRng As Range
Dim cel As Range
Dim Curr() As String
Dim cMatch As Variant
Dim n As Long
Dim isFound As Boolean
For Each aRng In rng.Areas
For Each cel In aRng.Cells
If Not IsError(cel) Then
Curr = Split(cel.Value, Delimiter)
For n = 0 To UBound(Curr)
cMatch = Application.Match(Curr(n), Roles, 0)
If IsError(cMatch) Then
isFound = True
Exit For
Else
If StrComp(Curr(n), Roles(cMatch - 1), _
vbBinaryCompare) <> 0 Then
isFound = True
Exit For
End If
End If
Next n
If isFound Then
isFound = False
If dRng Is Nothing Then
Set dRng = cel
Else
Set dRng = Union(dRng, cel)
End If
End If
End If
Next cel
Next aRng
Application.ScreenUpdating = False
rng.Interior.Color = xlNone
If Not dRng Is Nothing Then
dRng.Interior.Color = vbRed
End If
Application.ScreenUpdating = True
End Sub
I did put your code inside a module2 and also in an active worksheet:
Option Explicit
Sub Sample()
Dim ws As Worksheet
Dim lrow As Long
Dim rng As Range
Dim sAddr As String
Set ws = Sheet1
With ws
lrow = .Range("L" & .Rows.Count).End(xlUp).Row
Set rng = .Range("L2:L" & lrow)
sAddr = rng.Address
rng = Evaluate("index(IF(RIGHT(TRIM(" & sAddr & _
"),2)=""||"",LEFT(TRIM(" & sAddr & _
"),LEN(TRIM(" & sAddr & _
"))-2)," & sAddr & _
"),)")
End With
End Sub
For some reason, module2 won't work I suspect module1 to interfere with it indeed but can't find a solution.
My whole code looks like this:
Sub AllInOne()
Application.EnableEvents = True
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
Range("F2:F" & Cells(rows.Count, "F").End(xlUp).Row).Copy Destination:=Range("J2")
Range("F2:F" & Cells(rows.Count, "F").End(xlUp).Row).Copy Destination:=Range("K2")
ActiveSheet.Hyperlinks.Delete
For Each rng In Range("F2:F" & Cells(rows.Count, "F").End(xlUp).Row): rng.Value = LCase(rng.Value): Next rng
For Each rng In Range("K2:K" & Cells(rows.Count, "K").End(xlUp).Row): rng.Value = LCase(rng.Value): Next rng
For Each rng In Range("J2:J" & Cells(rows.Count, "J").End(xlUp).Row): rng.Value = LCase(rng.Value): Next rng
Dim cell As Range
lastRow = ActiveSheet.Cells(ActiveSheet.rows.Count, "C").End(xlUp).Row
For Each cell In ActiveSheet.Range("C2:C" & lastRow)
S = vbNullString
If cell.Value <> vbNullString Then
v = Split(cell.Value, " ")
For Each W In v
S = S & Left$(W, 1) & "."
Next W
cell.Offset(ColumnOffset:=-1).Value = S
End If
Next cell
Application.Range("B1").Value = "tesing"
Worksheets("Sheet1").Range("B1").Font.Bold = True
Columns("D").Replace What:="vander", _
Replacement:="van der", _
LookAt:=xlPart, _
SearchOrder:=xlByRows, _
MatchCase:=False, _
SearchFormat:=False, _
ReplaceFormat:=False
Columns("D").Replace What:="vanden", _
Replacement:="van den", _
LookAt:=xlPart, _
SearchOrder:=xlByRows, _
MatchCase:=False, _
SearchFormat:=False, _
ReplaceFormat:=False
Columns("B").Replace What:="..", _
Replacement:=".", _
LookAt:=xlPart, _
SearchOrder:=xlByRows, _
MatchCase:=False, _
SearchFormat:=False, _
ReplaceFormat:=False
Dim r As Range
For Each r In ActiveSheet.UsedRange
If Not IsError(r.Value) Then
v = r.Value
If v <> vbNullString Then
If Not r.HasFormula Then
r.Value = Trim(v)
End If
End If
End If
Next r
Dim i As Long
Dim DelRange As Range
On Error GoTo Whoa
Application.ScreenUpdating = False
For i = 1 To 50
If Application.WorksheetFunction.CountA(Range("A" & i & ":" & "Z" & i)) = 0 Then
If DelRange Is Nothing Then
Set DelRange = Range("A" & i & ":" & "Z" & i)
Else
Set DelRange = Union(DelRange, Range("A" & i & ":" & "Z" & i))
End If
End If
Next i
If Not DelRange Is Nothing Then DelRange.Delete shift:=xlUp
LetsContinue:
Application.ScreenUpdating = True
Exit Sub
Whoa:
MsgBox Err.Description
Resume LetsContinue
Worksheets("Sheet1").Columns("L").Replace _
What:=" ", _
Replacement:="", _
SearchOrder:=xlByColumns, _
MatchCase:=True
Application.EnableEvents = True
Application.ScreenUpdating = True
Application.Calculation = xlCalculationAutomatic
End Sub
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Range)
Const RolesList As String = "Testing"
Const FirstCellAddress As String = "L2"
Const Delimiter As String = "||"
Dim rng As Range
With Range(FirstCellAddress)
Set rng = Intersect(.Resize(.Worksheet.rows.Count - .Row + 1), Target)
End With
If rng Is Nothing Then
Exit Sub
End If
Dim Roles() As String: Roles = Split(RolesList, ",")
Dim dRng As Range
Dim aRng As Range
Dim cel As Range
Dim Curr() As String
Dim cMatch As Variant
Dim n As Long
Dim isFound As Boolean
For Each aRng In rng.Areas
For Each cel In aRng.Cells
If Not IsError(cel) Then
Curr = Split(cel.Value, Delimiter)
For n = 0 To UBound(Curr)
cMatch = Application.Match(Curr(n), Roles, 0)
If IsError(cMatch) Then
isFound = True
Exit For
Else
If StrComp(Curr(n), Roles(cMatch - 1), _
vbBinaryCompare) <> 0 Then
isFound = True
Exit For
End If
End If
Next n
If isFound Then
isFound = False
If dRng Is Nothing Then
Set dRng = cel
Else
Set dRng = Union(dRng, cel)
End If
End If
End If
Next cel
Next aRng
Application.ScreenUpdating = False
rng.Interior.Color = xlNone
If Not dRng Is Nothing Then
dRng.Interior.Color = vbRed
End If
Application.ScreenUpdating = True
End Sub
I am trying to insert a blank row above a row that contains a specific word. But so far I can only insert it below this row.
Sub INSERTROW()
Dim c As Range
Dim lRow As Long
lRow = 1
Dim lRowLast As Long
Dim bFound As Boolean
With ActiveSheet
lRowLast = .Cells(.Rows.Count, 1).End(xlUp).Row
Do
Set c = .Range("A" & lRow)
If c.Value Like "*SEARCHED VALUE*" Then
bFound = True
ElseIf bFound Then
bFound = False
If c.Value <> "BLANKROW" Then
c.EntireRow.Insert
lRowLast = lRowLast + 1
c.Offset(-1, 0).Value = "BLANKROW"
c.Offset(-1, 0).Font.Color = RGB(0, 0, 0)
End If
End If
lRow = lRow + 1
Loop While lRow <= lRowLast + 1
End With
End Sub
It's easier and faster (AFAIK) if you use the find method.
See that I insert the row where the value is found and then I refer to the previous row with the offset function.
Finally, as a good practice, try to name your procedures and variables to something meaningful and indent your code (you may use www.rubberduckvba.com)
Public Sub InsertRowBeforeWord()
Dim findString As String
findString = "*SEARCHED VALUE*"
Dim lastRow As Long
lastRow = ActiveSheet.Cells(ActiveSheet.Rows.Count, 1).End(xlUp).Row
Dim searchRange As Range
Set searchRange = ActiveSheet.Range("A1:A" & lastRow)
Dim returnRange As Range
Set returnRange = searchRange.Find(What:=findString, _
After:=searchRange.Cells(searchRange.Cells.Count), _
LookIn:=xlValues, _
LookAt:=xlWhole, _
SearchOrder:=xlByRows, _
SearchDirection:=xlNext, _
MatchCase:=False)
If Not returnRange Is Nothing Then
returnRange.Offset(0, 0).EntireRow.Insert
returnRange.Offset(-1, 0).Value = "BLANKROW"
returnRange.Offset(-1, 0).Font.Color = RGB(0, 0, 0)
End If
End Sub
Let me know if it works.
I have this code, but I'd like for it to find two adjacent cells with the values 7 and 2 (7 first in every pair) in column A and offset (from the 7) to the next column and insert a value to a specific row range.
Sub mark()
Dim FirstAddress As String
Dim MyArr As Variant
Dim Rng As Range
Dim I As Long
With Application
.ScreenUpdating = False
.EnableEvents = False
End With
MyArr = Array("X")
With Sheets("Sheet1").Range("A:A")
.Offset(0, 1).ClearContents
For I = LBound(MyArr) To UBound(MyArr)
Set Rng = .Find(What:=MyArr(I), _
After:=.Cells(.Cells.Count), _
LookIn:=xlFormulas, _
LookAt:=xlWhole, _
SearchOrder:=xlByRows, _
SearchDirection:=xlNext, _
MatchCase:=False)
If Not Rng Is Nothing Then
FirstAddress = Rng.Address
Do
Rng.Offset(0, 1).Value = "X"
Set Rng = .FindNext(Rng)
Loop While Not Rng Is Nothing And Rng.Address <> FirstAddress
End If
Next I
End With
With Application
.ScreenUpdating = True
.EnableEvents = True
End With
End Sub
Any suggestions appreciated.
Please try this code. I have expanded it to provide for specifying different results to be inserted at the found location.
Sub Mark2()
' 14 Feb 2018
Dim Ws As Worksheet
Dim Crits() As Variant
Dim Fun() As Variant
Dim MarkRng As Range
Dim R As Long
Dim i As Long
Dim Count As Integer
Set Ws = Worksheets("FindPair") ' replace with actual name
Crits = Array(7, 2, 13, 3, 17, 4) ' 1st, 2nd, 1st, 2nd, 1st, 2nd criterium
' Match the ranges in Fun() to the targets in Crits()
' 2 ranges for each Crit, each range 1 or more cells
' Omitted ranges must be represented by a comma
' Fun ranges specified in excess of available space will be ignored
' (for example, A32 + B28:B32 = 6 cells but MarkRng has only 5 cells)
Fun = Array("A32", "B28:B32", "A33:A35", "B33:B34", , "B100:B104")
With Application
.ScreenUpdating = False
.EnableEvents = False
End With
For i = LBound(Crits) To UBound(Crits) Step 2
Count = 0
Do
R = MatchRow(Crits(i), Ws, (R = 0))
If R Then
With Ws
If .Cells(R + 1, 1).Value = Crits(i + 1) Then
' column 2 = column B
Set MarkRng = Range(.Cells(R, 2), .Cells(R + 5, 2))
WriteResult i, Fun, MarkRng
Count = Count + 1
End If
End With
Else
If Count = 0 Then
MsgBox "No match was found for " & Crits(i), _
vbInformation, "Failure advice"
End If
Exit Do
End If
Loop
Next i
With Application
.ScreenUpdating = True
.EnableEvents = True
End With
End Sub
Private Function MatchRow(ByVal Crit As Variant, _
Ws As Worksheet, _
ByVal NewSearch As Boolean) As Long
' 13 Feb 2018
Static Rng As Range
Static Rstart As Long
Static Rend As Long
Dim Fnd As Range
With Ws
If NewSearch Then
Rstart = 2 ' start search in row 2
' find last used row
Rend = .Cells(.Rows.Count, 1).End(xlUp).Row
End If
Set Rng = Range(.Cells(Rstart, 1), .Cells(Rend, 1))
End With
With Rng
Set Fnd = .Find(What:=Crit, _
After:=.Cells(.Cells.Count), _
LookIn:=xlFormulas, _
LookAt:=xlWhole, _
SearchOrder:=xlByRows, _
SearchDirection:=xlNext, _
MatchCase:=False)
End With
If Not Fnd Is Nothing Then
MatchRow = Fnd.Row
Rstart = Fnd.Row + 1
End If
End Function
Private Sub WriteResult(ByVal Ix As Long, _
Fun() As Variant, _
Target As Range)
' 14 Feb 2018
Dim Ws As Worksheet
Dim Rng As Range, R As Long ' source
Dim Rt As Long
Dim i As Long
With Target
Set Ws = .Worksheet
For i = 0 To 1
If Not IsError(Fun(Ix + i)) Then
Set Rng = Ws.Range(Fun(Ix + i))
For R = 1 To Rng.Cells.Count
If Rt < .Cells.Count Then
Rt = Rt + 1
.Cells(Rt).Value = Rng.Cells(R).Value
End If
Next R
End If
Next i
End With
End Sub
I'm new here and at VBA.
My question goes:
I have 3 sheets(1, 2 and 3). At sheet 1 I have column A(range A2-end) with data that I want to compare with column A(range A2-end) and D(range D2-end) on sheet 2. If a value in sheet 1 column A is not found on sheet 2column A and D, then it should list the mismatched value in sheet 3 starting at Range A2.
Here is what I have:
Sub Makro5()
Dim lastRowE As Integer
Dim lastRowF As Integer
Dim lastRowM As Integer
Dim foundTrue As Boolean
Application.ScreenUpdating = False
lastRowE = Sheets("1").Cells(Sheets("1").Rows.Count, "A2").End(xlUp).row
lastRowE = Sheets("2").Cells(Sheets("2").Rows.Count, "A2").End(xlUp).row
lastRowF = Sheets("2").Cells(Sheets("2").Rows.Count, "D2").End(xlUp).row
lastRowM = Sheets("3").Cells(Sheets("3").Rows.Count, "A2").End(xlUp).row
For i = 1 To lastRowE
foundTrue = False
For j = 1 To lastRowF
If Sheets("1").Cells(i, 1).value = Sheets("2").Cells(j, 1).value Then
foundTrue = True
and
If Sheets("1").Cells(i, 1).value = Sheets("2").Cells(j, 4).value Then
foundTrue = True
Exit For
End If
Next j
If Not foundTrue Then
Sheets("3").Rows(i).Copy Destination:= _
Sheets("3").Rows(lastRowM + 1)
lastRowM = lastRowM + 1
End If
This reads col A and D from Sheet2 in a dictionary
Then searches for values in col A of Sheet1 in the dictionary
Items not found are placed in Sheet3, starting at cell A2
Option Explicit
Public Sub FindMissing()
Dim ws1 As Worksheet, colA1 As Variant, r As Long, d1 As Object, d2 As Object
Dim ws2 As Worksheet, colA2 As Variant, colD2 As Variant, ws3 As Worksheet
Set d1 = CreateObject("Scripting.Dictionary")
Set d2 = CreateObject("Scripting.Dictionary")
Set ws1 = ThisWorkbook.Worksheets("Sheet1")
Set ws2 = ThisWorkbook.Worksheets("Sheet2")
Set ws3 = ThisWorkbook.Worksheets("Sheet3")
colA1 = ws1.Range("A2:A" & ws1.Cells(ws1.Rows.Count, 1).End(xlUp).Row) 'Sheet1.colA
colA2 = ws2.Range("A2:A" & ws2.Cells(ws2.Rows.Count, 1).End(xlUp).Row) 'Sheet2.colA
colD2 = ws2.Range("D2:D" & ws2.Cells(ws2.Rows.Count, 4).End(xlUp).Row) 'Sheet2.colD
If Not IsArray(colA1) Then MakeArray colA1 'Sheet1.colA contains only 1 row
If Not IsArray(colA2) Then MakeArray colA2 'Sheet2.colA contains only 1 row
If Not IsArray(colD2) Then MakeArray colD2 'Sheet2.colD contains only 1 row
For r = 1 To UBound(colA2)
d1(colA2(r, 1)) = vbNullString 'read Sheet2.ColA in dictionary d1.Keys
Next
For r = 1 To UBound(colD2)
d1(colD2(r, 1)) = vbNullString 'read Sheet2.ColD in dictionary d1.Keys
Next
For r = 1 To UBound(colA1) 'search vals from Sheet1.colA in dictionary d1
If Not d1.Exists(colA1(r, 1)) Then d2(colA1(r, 1)) = vbNullString
Next
ws3.Columns(1).Delete
If d2.Count > 0 Then ws3.Cells(2, 1).Resize(d2.Count, 1) = Application.Transpose(d2.Keys)
End Sub
Private Sub MakeArray(ByRef arr As Variant)
Dim tmp As Variant
tmp = arr
ReDim arr(1 To 1, 1 To 1)
arr(1, 1) = tmp
End Sub
Try using this code below...
Public Function Find_First(FindString As String, WithinRange As Range) As Boolean
Dim rng As Range
Find_First = False
If Trim(FindString) <> "" Then
With WithinRange
Set rng = .Find(What:=FindString, _
After:=.Cells(.Cells.Count), _
LookIn:=xlValues, _
LookAt:=xlWhole, _
SearchOrder:=xlByRows, _
SearchDirection:=xlNext, _
MatchCase:=False)
If Not rng Is Nothing Then
Find_First = True
End If
End With
End If
End Function
I am currently building a macro to format a sheet of data as well as to remove inapplicable rows of data. Specifically, I am looking to delete rows where Column L = "ABC" as well as delete rows where Column AA <> "DEF".
So far I have been able to achieve the first objective, but not the second. The existing code is:
Dim LastRow As Integer
Dim x, y, z As Integer
Dim StartRow, StopRow As Integer
For x = 0 To LastRow
If (Range("L1").Offset(x, 0) = "ABC") Then
Range("L1").Offset(x, 0).EntireRow.Delete
x = x - 1
End If
It is normally much quicker to use AutoFilter rather than loop Ranges
The code below creates a working column, then use a formula to detect delete criteria and then autofilter and delete the result records
The working column puts a formula
=OR(L1="ABC",AA1<>"DEF")
into row 1 of the first blank column then copies down as far ar the true used range. Then any TRUE records are quicklly deleted with AutoFilter
Sub QuickKill()
Dim rng1 As Range, rng2 As Range, rng3 As Range
Set rng1 = Cells.Find("*", , xlValues, , xlByColumns, xlPrevious)
Set rng2 = Cells.Find("*", , xlValues, , xlByRows, xlPrevious)
Set rng3 = Range(Cells(rng2.Row, rng1.Column), Cells(1, rng1.Column))
Application.ScreenUpdating = False
Rows(1).Insert
With rng3.Offset(-1, 1).Resize(rng3.Rows.Count + 1, 1)
.FormulaR1C1 = "=OR(RC12=""ABC"",RC27<>""DEF"")"
.AutoFilter Field:=1, Criteria1:="TRUE"
.EntireRow.Delete
On Error Resume Next
'in case all rows have been deleted
.EntireColumn.Delete
On Error GoTo 0
End With
Application.ScreenUpdating = True
End Sub
Using a loop:
Sub test()
Dim x As Long, lastrow As Long
lastrow = Cells(Rows.Count, 1).End(xlUp).Row
For x = lastrow To 1 Step -1
If Cells(x, 12).Value = "ABC" or Cells(x, 27) <> "DEF" Then
Rows(x).Delete
End If
Next x
End Sub
Using autofilter (probably faster):
Sub test2()
Range("a1").AutoFilter Field:=12, Criteria1:="ABC", Operator:=xlOr, _
Field:=28, Criteria1:="<>""DEF"""
'exclude 1st row (titles)
With Intersect(Range("a1").CurrentRegion, _
Range("2:60000")).SpecialCells(xlCellTypeVisible)
.Rows.Delete
End With
ActiveSheet.ShowAllData
End Sub
Cell with number 12 is "L" and number 27 is "AA"
Dim x As Integer
x = 1
Do While x <= ActiveSheet.Cells.SpecialCells(xlCellTypeLastCell).Row
If (Cells(x, 12) = "ABC") Then
ActiveSheet.Rows(x).Delete
Else
If (Cells(x, 27) <> "DEF") And (Cells(x, 27) <> "") Then
ActiveSheet.Rows(x).Delete
Else
x = x + 1
End If
End If
Loop
End Sub
Sub test()
Dim bUnion As Boolean
Dim i As Long, lastrow As Long
Dim r1 As Range
Dim v1 As Variant
lastrow = Cells(Rows.Count, 1).End(xlUp).Row
v1 = ActiveSheet.Range(Cells(1, 12), Cells(lastrow, 27)).Value2
bUnion = False
For i = 1 To lastrow
If v1(i, 1) = "ABC" Or v1(i, 16) <> "DEF" Then
If bUnion Then
Set r1 = Union(r1, Cells(i, 1))
Else
Set r1 = Cells(i, 1)
bUnion = True
End If
End If
Next i
r1.EntireRow.Delete
End Sub