copying specific entries from table based on "name" (find function) vba - vba

I have a code that's intended to:
1) Find a name from a table using a searchbox
2) Copy cells in the row with the name on to another sheet
3) This should work for all entries in the table associated with this name.
Code sample:
Sub Printout()
Dim LR2 As Long
Dim c As Variant
Dim txt As Variant
c = InputBox("Enter Last Name")
txt = CStr(c)
Sheets("B").Select
Sheets("B").Range("K3").Value = txt
Sheets("A").Select
Sheets("A").Columns(2).Find(What:=txt, LookIn:=xlFormulas, LookAt:=xlWhole, SearchOrder:=xlByRows, SearchDirection:=xlNext, MatchCase:=True).Activate
LR2 = Sheets("A").Cells(Rows.Count, "a").End(xlUp).Row
Sheets("A").Range(Cells(ActiveCell.Row, 2), Cells(LR2, 10)).Select
Selection.Copy Destination:=Sheets("B").Range("A2:J2")
End Sub
Problem:
Currently, the code doesn't just copy the specific name from the searchbox input, but all entries under the name as well. Ie if "Johnson" is entry 3, 6, and 11, I want columns 2 to 10 for those three rows. Currently it finds the first entry and seem to copy everything in columns 2 to 10 underneath it. Can someone please help me troubleshoot this code so that I can make it do what I want?
Thanks in advance!

this should be what youre after. Season to taste but itll do what you want
Private Sub derp()
Dim this As String
this = InputBox("Enter Last Name")
Dim rng As Range
Dim rcell As Range
Dim lastrow As Long
Dim that As Variant
lastrow = ThisWorkbook.Sheets("Sheet3").UsedRange.Rows.Count
Set rng = ThisWorkbook.Sheets("Sheet2").Range("A1:a40")
For Each rcell In rng.Cells
If rcell.Value <> vbNullString Then
If rcell.Value = this Then
that = ThisWorkbook.Sheets("Sheet2").Range("A" & rcell.Row & ":H" & rcell.Row)
ThisWorkbook.Sheets("Sheet3").Range("A" & lastrow & ":H" & lastrow).Value2 = that
lastrow = lastrow + 1
End If
End If
Next rcell
End Sub

This is my best guess
Sub Printout()
Dim LR2 As Long
Dim c As Variant
Dim txt As Variant
Dim r As Range
Dim s As String
c = InputBox("Enter Last Name")
txt = CStr(c)
Sheets("B").Range("K3").Value = txt
With Sheets("A")
Set r = .Columns(2).Find(What:=txt, LookIn:=xlFormulas, LookAt:=xlWhole, SearchOrder:=xlByRows, SearchDirection:=xlNext, MatchCase:=True)
If Not r Is Nothing Then
s = r.Address
Do
LR2 = Sheets("B").Cells(Rows.Count, "a").End(xlUp).Row
.Range(.Cells(r.Row, 2), .Cells(r.Row, 10)).Copy Destination:=Sheets("B").Range("A" & LR2)
Set r = .Columns(2).FindNext(r)
Loop While r.Address <> s
End If
End With
End Sub

It is sometimes problematic to work with code, that is not created by you. In your case, you want to select and copy the cells, which you have found in column 2.
If you take a look at this code and edit it a bit, you would be able to do it.
Option Explicit
Option Private Module
Sub Printout()
Dim txt As Variant
Dim rngUnion As Range
Dim rngCell As Range
txt = "vi"
With ActiveSheet
For Each rngCell In .Range(.Cells(1, 1), .Cells(9, 1))
If InStr(1, rngCell, txt) Then
If rngUnion Is Nothing Then
Set rngUnion = .Range(.Cells(rngCell.Row, 2), .Cells(rngCell.Row, 5))
Else
Set rngUnion = Union(rngUnion, .Range(.Cells(rngCell.Row, 2), .Cells(rngCell.Row, 5)))
End If
End If
Next rngCell
End With
rngUnion.Select
End Sub
Your ActiveSheet should like this:
What the code does:
It loops through the Cells from A1 to A9.
If it finds vi in one of those cells, it adds 4 cells of the same row to a union - rngUnion
At the end it selects a union, just to show you which one is it. You can copy the selection or copy the range, without selecting it.
Good luck, have fun!

Related

Reverse Loop ignores some cells

I have written a small code that allow me to:
in a defined range (xrng) in column F, find all the cells that contain certain text and once found, select all the cells in the range A:G on the same row and delete them. I have a reverse loop, which work partially, as ignores some cells in the range, specifically the 2nd and the 3rd. Below a before and after pic:
Here my code:
Sub removeapp()
Dim g As Long, xrng As Range, lastrow As Long, i As Long
i = 4
lastrow = Cells(Rows.Count, "F").End(xlUp).Row
Set xrng = Range(Cells(lastrow, "F"), Cells(i, "F"))
For g = xrng.Count To i Step -1
If xrng.Cells(g).Value = "Adjustment" Or xrng.Cells(g).Value = "Approved" Then
Range(Cells(xrng.Cells(g).Row(), "A"), Cells(xrng.Cells(g).Row(), "G")).Delete
End If
Next
End Sub
Could you help me to figure out why?
Also, the code runs really slow... if you have any tip to make it slighlty faster would be great!
Try this, please:
Sub removeappOrig()
Dim xrng As Range, lastrow As Long, sh As Worksheet
Set sh = ActiveSheet 'good to put here your real sheet
lastrow = sh.Cells(sh.Rows.count, "F").End(xlUp).Row
Set xrng = sh.Range("A4:F" & lastrow)
xrng.AutoFilter field:=6, Criteria1:="=Adjustment", Operator:=xlOr, _
Criteria2:="=Approved", VisibleDropDown:=False
Application.DisplayAlerts = False
xrng.SpecialCells(xlCellTypeVisible).Delete
Application.DisplayAlerts = True
sh.AutoFilterMode = False
End Sub
The next code is also fast enough since it iterates between array elements (in memory), not deletes row by row (it creates a ranges Union) and delete all at once:
Private Sub remoRangesAtOnce()
Dim i As Long, lastRow As Long, sh As Worksheet
Dim arrF As Variant, rng As Range, rngDel As Range
Set sh = ActiveSheet 'please name it according to your sheet name
lastRow = sh.Cells(sh.Rows.count, "F").End(xlUp).Row
Set rng = sh.Range("F4:F" & lastRow)
arrF = rng.Value
For i = LBound(arrF) To UBound(arrF)
If arrF(i, 1) = "Adjustment" Or arrF(i, 1) = "Approved" Then
If rngDel Is Nothing Then
Set rngDel = sh.Range(sh.Range("A" & i + 3), sh.Range("F" & i + 3))
Else
Set rngDel = Union(rngDel, sh.Range(sh.Range("A" & i + 3), sh.Range("F" & i + 3)))
End If
End If
Next i
If Not rngDel Is Nothing Then rngDel.Delete xlShiftUp
End Sub

Remove Duplicates based on 2 columns in ObjectList

I want to remove duplicated values in my Table. It is ListObject Table with 1000s of lines.
I cannot get to work my code and I am using function RemoveDuplicates for ranges with the condition to delete the lines in the table if there are duplicates in 2 relative columns.
This is how it looks before running the code:
In my code, based on column C:C and E:E only, I need to check if each line has any duplicates within these 2 columns and delete them, leaving only one.
And this is my desired outcome:
This is my code that does not work. I am not sure if it is because my table is the List Object or because I assign Array wrong?
Sub test_Duplicate()
Dim endrow As Long
Dim rng As Range
Dim ws As Worksheet
Set ws = Sheets("Sheet4")
With ws
endrow = .Cells(.Rows.Count, 3).End(xlUp).Row
Set rng = .Range(.Cells(2, 3), .Cells(endrow, 6))
rng.RemoveDuplicates Columns:=Array(1, 3), Header:=xlNo
End With
End Sub
I will appreciate any help.
I noticed that whether my data is in the table as List Object or whether is it without it, I am getting the error nr 9 anyway.
This will work:
Just change the TableName ..Mine is Table1
Sub test_Duplicate()
Dim ws As Worksheet
Set ws = Sheets("Sheet4")
With ws
.Range("Table1[#All]").RemoveDuplicates Columns:=Array(1, 3), Header:=xlYes
End With
End Sub
Table Name is found on the Design Tab when you select the table.
Here is the best solution I managed to find by looking through web.
Sub RemoveDuplicates()
Const TEST_COLUMN As String = "A"
Dim i As Long
Dim iLastRow As Long
Dim rng As Range
Dim ws As Worksheet
Set ws = Worksheets("Sheet4")
With ws
iLastRow = .Cells(.Rows.Count, 1).End(xlUp).Row
For i = 1 To iLastRow
If .Evaluate("SUMPRODUCT(--(A" & i & ":A" & iLastRow & "=A" & i & ")," & _
"--(I" & i & ":I" & iLastRow & "=I" & i & "))") > 1 Then
If rng Is Nothing Then
Set rng = .Cells(i, "A").Resize(, 9)
Else
Set rng = Union(rng, .Cells(i, "A").Resize(, 9))
End If
End If
Next i
If Not rng Is Nothing Then rng.Delete
End With
End Sub

VBA Excel - changing case to Proper Case

I'm new to Stackoverflow so hopefully I have posted this question in the right place.
I'm having trouble getting my code to work in VBA. I want it to select columns D:F until the last cell value. With this selection, I would like to change the case of the cells (they are currently uppercase) to Proper case.
Dim Lastrow As Integer
Dim range As Variant
With Worksheets("Overdue PO")
Lastrow = .Cells(Rows.Count, "D").End(xlUp).Row
.range("D2:F" & Lastrow).Select
range = Selection.Value
End With
Application.Proper (range)
It currently selects the range until the bottom row, but it doesn't change the case of text. No error appears when running the code.
Thanks in advance :)
Try this:
Dim Lastrow As Integer
With Worksheets("Overdue PO")
Lastrow = .Cells(Rows.Count, "D").End(xlUp).Row
.Range("D2:F" & Lastrow).Value = .Evaluate("INDEX(PROPER(D2:F" & Lastrow & "),)")
End With
It will be near instant, without the need for loops.
Also using .Select or .Activate also slow down the code, aoid them if possible by referring to the cells directly.
Try this
Sub test()
Dim Lastrow As Integer
Dim range As range
Dim c As range
With Worksheets("Overdue PO")
Lastrow = Columns("D:F").Cells.Find(What:="*", LookIn:=xlValues, SearchOrder:=xlByColumns, SearchDirection:=xlPrevious).Row
.range("D2:F" & Lastrow).Select
Set range = Selection
End With
For Each c In range
c.Value = Application.WorksheetFunction.Proper(c.Value)
Next c
End Sub
Application.WorksheetFunction.Proper(range) should do it. See https://msdn.microsoft.com/en-us/library/office/ff834434.aspx for documentation on WorksheetFunction
I wanted to use this with a named range, and was kindly provided the following code. Perhaps it will assist someone else:
Sub m_MakeProper()
'2/8/2018
'
Application.Volatile True
Application.ScreenUpdating = False
Application.DisplayAlerts = False
Application.DisplayStatusBar = False
'
Worksheets("test").Activate
'
Dim LastRow As Long
Dim LastCol As Integer
Dim cell As Variant
Dim thiswksht As Worksheet
Dim thiswb As Workbook
'
'
Set thiswksht = ActiveSheet
If thiswksht.AutoFilterMode Then
AutoFilterMode = False
End If
'
With thiswksht
LastRow = Cells(Cells.Rows.Count, "A").End(xlUp).Row
LastCol = Cells(1, Columns.Count).End(xlToLeft).Column
End With
'
Cells(1, 1).Activate
Rows(1).find("Status").Select
Range(ActiveCell.Address, Cells(LastRow, ActiveCell.Column)).Select
Selection.Name = "c_P_Status"
With Range("c_P_Status")
.Value = Application.Evaluate("INDEX(PROPER(" & .Address & "),0)")
End With
'
End Sub

Selecting and Pasting Cells

I'm relatively new to VBA, I have only some experience with Python and only very little experience looking at other VBA macros and adjusting them to my need, so I'm trying to do what I can.
What I am trying to do is for each part number pasted in worksheet B (worksheet B, row A) I want to find the same part number from a different worksheet containing all part numbers (worksheet D, row A) and copy the description (worksheet D, row H) from worksheet D to another column, (worksheet B, row D) then check the next part number in the row and repeat.
The current error that I'm getting is that there is "Compile error: Else without if", I'm sorry that I am not very proficient, but any help would be greatly appreciated.
Other information:
-My part numbers to search through in worksheet B, column B are filled in from worksheet A, is it okay to just make it =A!B2 or =CONCATENATE(A!B2)?
Sub Description()
Dim wsA As Worksheet, wsB As Worksheet, wsC As Worksheet, wsD As Worksheet
Dim Rng As Range
Set wsB = Worksheets("B")
Set wsD = Worksheets("D")
Do: aRow = 2
If wsB.Cells(aRow, 2) <> "" Then
With Worksheets("D").Range("A:A")
x = wsB.Cells(aRow, 2)
Set Rng = .Find(What:=x, _
After:=.Cells(.Cells.Count), _
LookIn:=xlValues, _
LookAt:=xlWhole, _
SearchOrder:=xlByRows, _
SearchDirection:=xlNext, _
MatchCase:=False)
Selection.Copy
wsB.Cells(dRow, 2).Paste
dRow = dRow + 1
Else
aRow = aRow + 1
Loop Until wsB.Cells(aRow, 2) = ""
End Sub
Thanks again!
Edit: Can't Execute code in break mode is current error
Sub Description()
Dim wsA As Worksheet, wsB As Worksheet, wsC As Worksheet, wsD As Worksheet
Dim Rng As Range
Set wsB = Worksheets("B")
Set wsD = Worksheets("D")
aRow = 2
dRow = 2
Do:
If wsB.Cells(aRow, 1) <> "" Then
With Worksheets("D").Range("A:A")
Set Rng = .Find(What:=wsB.Cells(aRow, 1), _
After:=.Cells(.Cells.Count), _
LookIn:=xlValues, _
LookAt:=xlWhole, _
SearchOrder:=xlByRows, _
SearchDirection:=xlNext, _
MatchCase:=False)
Rng.Copy
Rng.Offset(0, 3).Paste (Cells(aRow, 4))
dRow = dRow + 1
aRow = aRow + 1
End With
End If
Loop Until wsB.Cells(aRow, 1) = ""
End Sub
Can you try to put End If on the next line after aRow = aRow + 1. See MSDN for syntax msdn.microsoft.com/en-us/library/752y8abs.aspx
In Excel we usually call vertical range as column, and horizontal one as row.
From your code and question description, I assume what you said "row A" is column A.
Also, your code scan through wsB.Cells(aRow, 2). It is column B not column A.
Anyway, this is just a minor problem.
The following code will check cells of column B of worksheet B. If the same value is found
in column A of worksheet D, then the cooresponding cell in column H of worksheet D will
be copied to the cell in column B of worksheet B.
Option Explicit
Sub Description()
Dim wsB As Worksheet, wsD As Worksheet, aRow As Long
Dim rngSearchRange As Range, rngFound As Range
Set wsB = Worksheets("B")
Set wsD = Worksheets("D")
Set rngSearchRange = wsD.Range("A:A")
aRow = 2
Do While wsB.Cells(aRow, 2).Value <> ""
Set rngFound = rngSearchRange.Find(What:=wsB.Cells(aRow, 2).Value, LookAt:=xlWhole)
If Not rngFound Is Nothing Then
wsD.Cells(rngFound.Row, 8).Copy Destination:=wsB.Cells(aRow, 4) ' Indexes of Column H, D are respectively 8, 4
End If
aRow = aRow + 1
Loop
End Sub
Here's what worked for me.
Sub Description()
Application.ScreenUpdating = False
Dim LastRow As Long
LastRow = Sheets("B").Cells.Find("*", SearchOrder:=xlByRows, SearchDirection:=xlPrevious).Row
Dim rng As Range
Dim foundRng As Range
For Each rng In Sheets("B").Range("B2:B" & LastRow)
Set foundRng = Sheets("D").Range("A:A").Find(rng, LookIn:=xlValues, lookat:=xlWhole)
If Not foundRng Is Nothing Then
Sheets("B").Cells(rng.Row, "D") = Sheets("D").Cells(foundRng.Row, "H")
End If
Next rng
Application.ScreenUpdating = True
End Sub

Excel VBA deleting entire row based on multiple conditions in a column

I am trying to write a macro in vba for excel. I want to delete every row that does not have at least one of three keywords in column D (keywords being "INVOICE", "PAYMENT", or "P.O."). I need to keep every row that contains these keywords. All other rows need to be deleted and the rows remaining need to be pushed to the top of the document. There are also two header rows that can not be deleted.
I found the code below but it deletes every row that does not contain "INVOICE" only. I can not manipulate the code to do what I need it to do.
Sub Test()
Dim ws As Worksheet
Dim rng1 As Range
Dim lastRow As Long
Set ws = ActiveWorkbook.Sheets("*Name of Worksheet")
lastRow = ws.Range("D" & ws.Rows.Count).End(xlUp).Row
Set rng = ws.Range("D1:D" & lastRow)
' filter and delete all but header row
With rng
.AutoFilter Field:=1, Criteria1:="<>*INVOICE*"
.Offset(2, 0).SpecialCells(xlCellTypeVisible).EntireRow.Delete
End With
' turn off the filters
ws.AutoFilterMode = False
End Sub
I would approach this loop slightly different. To me this is a bit easier to read.
Sub Test()
Dim ws As Worksheet
Dim lastRow As Long, i As Long
Dim value As String
Set ws = ActiveWorkbook.Sheets("*Name of Worksheet")
lastRow = ws.Range("D" & ws.Rows.Count).End(xlUp).Row
' Evaluate each row for deletion.
' Go in reverse order so indexes don't get messed up.
For i = lastRow To 2 Step -1
value = ws.Cells(i, 4).Value ' Column D value.
' Check if it contains one of the keywords.
If Instr(value, "INVOICE") = 0 _
And Instr(value, "PAYMENT") = 0 _
And Instr(value, "P.O.") = 0 _
Then
' Protected values not found. Delete the row.
ws.Rows(i).Delete
End If
Next
End Sub
The key here is the Instr function which checks for your protected keywords within the cell value. If none of the keywords are found then the If condition is satisfied and the row is deleted.
You can easily add additional protected keywords by just appending to the If conditions.
'similar with previous post, but using "like" operator
Sub test()
Dim ws As Worksheet, i&, lastRow&, value$
Set ws = ActiveWorkbook.ActiveSheet
lastRow = ws.Range("D" & ws.Rows.Count).End(xlUp).Row
For i = lastRow To 2 Step -1
value = ws.Cells(i, 4).value
' Check if it contains one of the keywords.
If Not (value Like "*INVOICE*" _
Or value Like "*PAYMENT*" _
Or value Like "*P.O.*") _
Then
' Protected values not found. Delete the row.
ws.Rows(i).Delete
End If
Next
End Sub
'
Sub test()
Dim i&
Application.ScreenUpdating = False
i = Range("D" & Rows.Count).End(xlUp).Row
While i <> 1
With Cells(i, 4)
If Not (.value Like "*INVOICE*" _
Or .value Like "*PAYMENT*" _
Or .value Like "*P.O.*") _
Then
Rows(i).Delete
End If
End With
i = i - 1
Wend
Application.ScreenUpdating = True
End Sub
The othe way is to insert an IF test in a working column, and then AutoFilter that.
This is the VBA equivalent of entering
=SUM(COUNTIF(D1,{"*INVOICE*","*PAYMENT*","*P.O.*"}))=0
and then deleting any row where none of these values are found in the corrresponing D cell
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 = "=SUM(COUNTIF(RC[-1],{""*INVOICE*"",""*PAYMENT*"",""*P.O.*""}))=0"
.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