Background
I have a master file which holds many sheets of data, and I have a list of requested changes which is constantly being updated. I need to write a macro such that it will run down each row in the Changes sheet and find its counterpart within the actual data sheets. I need to copy the relevant cells from the change sheet to the respective row where it exists in its particular sheet.
Information
Each observation has a general identifier in Column A (LOBID)
Also has a specific identifier in Column E (CourseCode)
Each pair is unique, as each CourseCode can exist within multiple sheets under multiple LOBIDs but will only pair with an LOBID once.
Sub InputChanges()
Dim changeWS As Worksheet: Dim destWS As Worksheet
Dim rngFound As Range: Dim strFirst As String
Dim LOBID As String: Dim CourseCode As String
Dim i As Integer: Dim LastRow As Integer
Const SHEET_NAMES As String = "Sheet A, Sheet B, Sheet C, etc."
Set changeWS = Sheets("Changes")
Application.DisplayAlerts = False
Application.ScreenUpdating = False
For Each destWS In ActiveWorkbook.Worksheets
If InStr(1, SHEET_NAMES, destWS.Name, vbBinaryCompare) > 0 Then
For i = 4 To changeWS.Range("A" & Rows.Count).End(xlUp).Row
LOBID = changeWS.Cells(i, 2)
CourseCode = changeWS.Cells(i, 5)
Set rngFound = Columns("A").Find(LOBID, Cells(Rows.Count, "A"), xlValues, xlWhole)
If Not rngFound Is Nothing Then
strFirst = rngFound.Address
Do
If Cells(rngFound.Row, "E").Value = CourseCode Then
Cells(rngFound.Row, "AP").Value = changeWS.Cells(i, 24).Value
End If
Set rngFound = Columns("A").Find(LOBID, rngFound, xlValues, xlWhole)
Loop While rngFound.Address <> strFirst
End If
Next i
End If
Next
Set rngFound = Nothing
Application.DisplayAlerts = True
Application.ScreenUpdating = True
End Sub
Here's my attempt so far, I have a feeling it's pretty off but I hope the logic at least makes sense. I am attempting to run through each row in the Changes sheet, search through all the Sheets (A, B, C, ... L) for LOBID, then for CourseCode. When a matching pair is found, I'm hoping to copy the value from the changeWS to the matched cell in the datasheet (there are many values to copy but I've left them out for code brevity). It doesn't throw any errors but it doesn't seem to do anything at all. If someone could at least nudge me in the right direction, I'd appreciate it.
Compiled but not tested:
Sub InputChanges()
Dim changeWS As Worksheet, rw As Range
Dim i As Integer
Set changeWS = ActiveWorkbook.Sheets("Changes")
Application.DisplayAlerts = False
Application.ScreenUpdating = False
For i = 4 To changeWS.Range("A" & Rows.Count).End(xlUp).Row
Set rw = GetRowMatch(CStr(changeWS.Cells(i, 2)), CStr(changeWS.Cells(i, 5)))
If Not rw Is Nothing Then
rw.Cells(1, "AP").Value = changeWS.Cells(i, 24).Value
changeWS.Cells(i, 2).Interior.Color = vbGreen
Else
changeWS.Cells(i, 2).Interior.Color = vbRed
End If
Next i
Application.DisplayAlerts = True
Application.ScreenUpdating = True
End Sub
Function GetRowMatch(LOBID As String, CourseCode As String) As Range
Dim arrSheets, s, sht As Worksheet, rv As Range, f As Range
Dim addr1 As String
arrSheets = Array("Sheet A", "Sheet B", "Sheet C") ', etc.")
For Each s In arrSheets
Set s = ActiveWorkbook.Sheets(s)
Set f = s.Columns(1).Find(LOBID, Cells(Rows.Count, "A"), xlValues, xlWhole)
If Not f Is Nothing Then
addr1 = f.Address()
Do
If f.EntireRow.Cells(5) = CourseCode Then
Set GetRowMatch = f.EntireRow 'return the entire row
Exit Function
End If
Set f = s.Columns(1).Find(LOBID, f, xlValues, xlWhole)
Loop While f.Address() <> addr1
End If
Next s
'got here with no match - return nothing
Set GetRowMatch = Nothing
End Function
Related
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
I am trying to update record on all worksheets in a workbook.
My search values are in column No 6 and the replace values are in column No 9
The code is only working for a worksheet not the entire worksheet at a time.
I tried this below but it is giving me an error
Sub AllWorksheetsLoop()
Dim WS_Count As Integer
Dim I As Integer
' Set WS_Count equal to the number of worksheets in the active
' workbook.
WS_Count = ActiveWorkbook.Worksheets.count
' Begin the loop.
For I = 1 To WS_Count
Dim N As Long
Dim count As Long
N = Cells(Rows.count, 1).End(xlUp).Row
For count = 1 To N
v1 = Cells(count, 6).Value
If v1 = "Palm Tree (M)" Then Cells(count, 9).Value = "='Project Comp Rate Akwa Ibom'!K7"
Next count
Next I
End Sub
Kindly assists pls
It sounds like you want something like, search column F of each worksheet for "Palm Tree (M)"; if found, enter the stated formula in the cell, on the same row, 3 columns to the right. I assume that you exclude the sheet referenced in the formula from being searched.
Option Explicit
Public Sub FindThatPhrase()
Application.ScreenUpdating = False
Dim ws As Worksheet, found As Range
For Each ws In ThisWorkbook.Worksheets
If ws.Name <> "Project Comp Rate Akwa Ibom" Then
Set found = GetAllMatches("Palm Tree (M)", ws.Columns("F"))
If Not found Is Nothing Then
Debug.Print ws.Name, found.Address
found.Offset(, 3) = "='Project Comp Rate Akwa Ibom'!$K$7"
End If
End If
Next ws
Application.ScreenUpdating = True
End Sub
Public Function GetAllMatches(ByVal findString As String, ByVal searchRng As Range) As Range
Dim foundCell As Range
Dim gatheredRange As Range
With searchRng
Set foundCell = searchRng.Find(findString)
Set gatheredRange = foundCell
Dim currMatch As Long
For currMatch = 1 To WorksheetFunction.CountIf(.Cells, findString)
Set foundCell = .Find(What:=findString, After:=foundCell, _
LookIn:=xlValues, Lookat:=xlWhole, SearchOrder:=xlByRows, _
SearchDirection:=xlNext, MatchCase:=False)
If Not gatheredRange Is Nothing Then
Set gatheredRange = Union(gatheredRange, foundCell)
Else
Set gatheredRange = foundCell
End If
Next currMatch
End With
Set GetAllMatches = gatheredRange
End Function
I am using the following vba code to copy values from column G in work book B and paste them into workbook A - where values match.
Workbook B contains the following:
Column C Column D Column E Column G
21/12/2016 123 444 100
12/12/2016 111 555 100
11/11/2014 123 444 0
Workbook A
Column D Column G Column J Column AX
21/12/2016 123 444
12/12/2016 111 555
11/11/2014 123 444
Essentially the value from workbook B, column G corresponding to each matching value needs to end up in column AX on workbook A.
The dates represent delivery dates.
The values in column G are quantities delivered.
The code works most of the time, except sometimes i have more than one occurrence of each item number in column D/J.
Sometimes i am getting the wrong results. i.e. where the item number in row 1 is 444 and then occurs again in row 3. the code will check the wrong delivery date or the wrong quantities delivered for these item numbers.
This is because my code does not ensure the values all match in the same line. I need it to do this.
Option Explicit
Option Compare Text
Sub Expecting()
ActiveSheet.EnableCalculation = False
Application.ScreenUpdating = False
Application.DisplayStatusBar = False
Application.EnableEvents = False
ActiveSheet.DisplayPageBreaks = False
Dim app As New Excel.Application
app.Visible = False 'Visible is False by default, so this isn't necessary
Dim oCell As Range, oCell2 As Range, oCell3 As Range, oCell4 As Range, targetCell As Range
Dim ws2 As Worksheet
Dim lastRow As Long
If IsFileOpen("\\gb-ss04\001_DATA\WH DISPO\(5) WH SHARED DRIVE\(21) WAREHOUSE RECEIVINGS\Order Checker.xlsm") Then
Else
Workbooks.Open "\\gb-ss04\001_DATA\WH DISPO\(5) WH SHARED DRIVE\(21) WAREHOUSE RECEIVINGS\Order Checker.xlsm"
End If
If Not GetWb("Order Checker", ws2) Then Exit Sub
lastRow = Range("J" & Rows.Count).End(xlUp).Row
With ws2
For Each targetCell In Range("J6:J" & lastRow)
Set oCell = .Range("D1", .Cells(.Rows.Count, "D").End(xlUp)).Find(what:=targetCell.Value, LookIn:=xlValues, lookat:=xlWhole)
Set oCell2 = .Range("C1", .Cells(.Rows.Count, "C").End(xlUp)).Find(what:=targetCell.Offset(0, -3).Value, LookIn:=xlValues, lookat:=xlWhole)
Set oCell3 = .Range("E1", .Cells(.Rows.Count, "E").End(xlUp)).Find(what:=CStr(targetCell.Offset(0, -6)), LookIn:=xlValues, lookat:=xlWhole)
If Not oCell Is Nothing And Not oCell2 Is Nothing And Not oCell3 Is Nothing Then
Application.EnableEvents = False
If oCell.Offset(0, 3) <> "0 / 0" Then
targetCell.Offset(0, 12).Value = oCell.Offset(0, 3)
Else
targetCell.Offset(0, 12).Value = "0"
End If
Application.EnableEvents = True
End If
Next
End With
Application.ScreenUpdating = True
Application.DisplayStatusBar = True
Application.EnableEvents = True
End Sub
Function GetWb(wbNameLike As String, ws As Worksheet) As Boolean
Dim wb As Workbook
For Each wb In Workbooks
If wb.Name Like "*" & wbNameLike & "*" Then '<-- check if workbook name contains "Depot Memo"
Set ws = wb.Worksheets(2)
Exit For
End If
Next
GetWb = Not ws Is Nothing
End Function
Please can someone show me where i am going wrong?
Your code is erroneous because of unqualified ranges. consider what happens when you open the checker workbook: it becomes the Active workbook and all unqualified ranges will go to it! When when you do this:
For Each targetCell In Range("J6:J" & lastRow) ' <~~ refers to ActiveWorkbook!
With ws2
Set oCell = .Range("D1", .Cells(.Rows.Count, "D").End(xlUp)).Find(what:=targetCell.Value, LookIn:=xlValues, lookat:=xlWhole)
in the find above, you are comparing the recently opened workbook to itself. It did not happen before as you say, yes, because before, the WB was already open so you did not open it again, so it did not steel the ActiveWorkbook property! As I told you in a previous comment, random behavior is typical when you use unqualified ranges, because they refer to the Active things (wb, ws).
The other error was that you were not ensuring that the matched values are on the same row. The following will do, though might need some customization to your files' structure (position of the worksheets and ranges)
Option Explicit
Sub Expecting()
Application.ScreenUpdating = False
Application.EnableEvents = False
Dim wbChecker As Workbook
On Error Resume Next
Set wbChecker = Workbooks("Order Checker.xlsm")
If wbChecker Is Nothing Then Set wbChecker = Workbooks.Open("\\gb-ss04\001_DATA\WH DISPO\(5) WH SHARED DRIVE\(21) WAREHOUSE RECEIVINGS\Order Checker.xlsm")
If wbChecker Is Nothing Then Exit Sub
On Error GoTo cleanup
Dim ws1 As Worksheet: Set ws1 = ThisWorkbook.Worksheets(1)
Dim ws2 As Worksheet: Set ws2 = wbChecker.Worksheets(1)
Dim lastRow1 As Long, lastRow2 As Long, ro1 As Long, ro2 As Long
lastRow1 = ws1.Range("J" & ws1.Rows.Count).End(xlUp).Row
lastRow2 = ws2.Range("D" & ws2.Rows.Count).End(xlUp).Row
For ro2 = 1 To lastRow2
For ro1 = 6 To lastRow1
If ws1.Cells(ro1, "D").Value = ws2.Cells(ro2, "C").Value And _
ws1.Cells(ro1, "G").Value = ws2.Cells(ro2, "D").Value And _
ws1.Cells(ro1, "J").Value = ws2.Cells(ro2, "E").Value Then _
ws1.Cells(ro1, "AX").Value = IIf(ws2.Cells(ro2, "G").Value <> "0 / 0", ws2.Cells(ro2, "G").Value, "0")
Next
Next
cleanup:
Application.ScreenUpdating = True
Application.EnableEvents = True
End Sub
I'm struggling with the following code which you can see below. It is totally a pain in the *** now. I really need some help.
This code is a search tool which looks for criteria from every worksheet except the summary and the list. After the .Find founds the word, then the code selects a 4 wide range around the searched word, then it copies and pastes it on the Summary sheet.
When the first searched word is found, I also would like to copy and paste the actual worksheet (where the word is found) title (on each worksheet "G3:J3") right after the search result on the summary page. This search tool could help me to find quickly which search criteria where can be found, at which sheet and some properties which also inside the title.
The result should look like this: (r1 = the first 4 columns, r2= the rest 4 columns (that is the excel header))
item nr. Item Owner Used Capacity ESD_nr. box Owner Free capacity location
Sorry for the long description.
CODE:
Private Sub searchTool()
Dim ws As Worksheet, OutputWs As Worksheet, wbName As Worksheet
Dim rFound As Range, r1 As Range, r2 As Range, multiRange As Range
Dim strName As String
Dim count As Long, lastRow As Long
Dim IsValueFound As Boolean
IsValueFound = False
Set OutputWs = Worksheets("Summary") '---->change the sheet name as required
lastRow = OutputWs.Cells(Rows.count, "K").End(xlUp).Row
On Error Resume Next
strName = ComboBox1.Value
If strName = "" Then Exit Sub
For Each ws In Worksheets
If ws.Name <> "lists" And ws.Name <> "Summary" Then
With ws.UsedRange
Set rFound = .Find(What:=strName, LookIn:=xlValues, LookAt:=xlWhole)
If Not rFound Is Nothing Then
firstAddress = rFound.Address
Do
IsValueFound = True
Set r1 = Range(rFound.EntireRow.Cells(1, "B"), rFound.EntireRow.Cells(1, "D"))
Set r2 = Range("G3:J3")
Set multiRange = Application.Union(r1, r2)
multiRange.Copy
OutputWs.Cells(lastRow + 1, 11).PasteSpecial xlPasteAll
Application.CutCopyMode = False
lastRow = lastRow + 1
Set rFound = .FindNext(rFound)
Loop While Not rFound Is Nothing And rFound.Address <> firstAddress
End If
End With
End If
Next ws
On Error GoTo 0
If IsValueFound Then
OutputWs.Select
MsgBox "Seach complete!"
Else
MsgBox "Name not found!"
End If
End Sub
I must admit I had trouble following your requirements and there was not a definition of where it wasn't working, to that end I re-wrote it to help me understand.
Private Sub SearchTool_2()
Dim BlnFound As Boolean
Dim LngRow As Long
Dim RngFind As Excel.Range
Dim RngFirstFind As Excel.Range
Dim StrName As String
Dim WkShtOutput As Excel.Worksheet
Dim WkSht As Excel.Worksheet
StrName = "Hello" 'ComboBox1.Value
If StrName = "" Then Exit Sub
Set WkShtOutput = ThisWorkbook.Worksheets("Summary")
LngRow = WkShtOutput.Cells(WkShtOutput.Rows.count, "K").End(xlUp).Row + 1
For Each WkSht In ThisWorkbook.Worksheets
If (WkSht.Name <> "lists") And (WkSht.Name <> "Summary") Then
With WkSht.UsedRange
Set RngFind = .Find(What:=StrName, LookIn:=xlValues, LookAt:=xlWhole)
If Not RngFind Is Nothing Then
Set RngFirstFind = RngFind
BlnFound = True
Do
WkSht.Range(RngFind.Address & ":" & WkSht.Cells(RngFind.Row, RngFind.Column + 2).Address).Copy WkShtOutput.Range(WkShtOutput.Cells(LngRow, 11).Address)
WkSht.Range("G3:J3").Copy WkShtOutput.Range(WkShtOutput.Cells(LngRow + 1, 11).Address)
LngRow = LngRow + 2
Set RngFind = .FindNext(RngFind)
Loop Until RngFind.Address = RngFirstFind.Address
End If
End With
End If
Next
Set WkShtOutput = Nothing
If BlnFound Then
ThisWorkbook.Worksheets("Summary").Select
MsgBox "Seach complete!"
Else
MsgBox "Name not found!"
End If
End Sub
I found the copy statement was the better option rather than using the clipboard, I also found a missing reference of firstAddress.
I have a worksheet (Sheet2) that contains 27 columns, first row is the columns headers which are A-Z and NUM totaling 27 cols. Each column has a very long list of restricted urls sorted to the letter of the column, and the last (27th) column is for urls that start with a number. The columns' length is between 300-600 thousand cells.
What I have been looking for was a macro script that will examine all newly added urls in col A Sheet1, to find out whether they exist in Sheet2, resulting in flagging each url with "already exist" or "to be added", something like:
Sheet1
Col(A) Col(B)
badsite1.com already exist
badsite2.com already exist
badsite3.com to be added
badsite4.con to be added
badsite5.com already exist
Accordingly "to be added" urls will be added to Sheet2 after running another test online for that url.
Amazingly, I found the following script (missed its source) that does exactly what I'm after applying some minor modifications:
Sub x()
Dim rFind As Range, sFind As Range, sAddr As String, ws As Worksheet, rng As Range, ms As Worksheet
Application.ScreenUpdating = 0
Set ws = Sheets("Sheet2")
Set ms = Sheets("Sheet1")
ms.Range("B2:B" & Rows.Count).ClearContents
Set rng = ms.Range("A2:A" & Cells(Rows.Count, 1).End(xlUp).Row)
For Each sFind In rng
With ws.UsedRange
Set rFind = .Find(sFind, .Cells(.Cells.Count), xlValues, xlPart)
If Not rFind Is Nothing Then
sAddr = rFind.Address
Do
sFind.Offset(, 1) = rFind.Address
sFind.Font.Color = -16776961
Set rFind = .FindNext(rFind)
Loop While rFind.Address <> sAddr
sAddr = ""
Else
sFind.Offset(, 1) = "No Found"
sFind.Offset(, 1).Font.Color = -16776961
End If
End With
Next
Set ms = Nothing
Set ws = Nothing
Set rng = Nothing
Set rFind = Nothing
Application.ScreenUpdating = True
End Sub
Running this script is fantastic with a small list of urls (e.g 5-10). With a longer list in Sheet1 col-A and HUGE lists in Sheet2 like mine, this script is a "tortoise", and it took over one hour to examine a list of 167 urls!!
Can this script be modified to be a "rabbit"? :)
Highly appreciating any offered assistance in this regard.
As usual.. thanks in advance.
Try this - Tested in Excel 2010:
Sub x()
Dim rFind As Range, sFind As Range, sAddr As String, ws As Worksheet
Dim rng As Range, ms As Worksheet, s As String
Application.ScreenUpdating = False
'stop calculation
Application.Calculation = xlCalculationManual
Set ws = Sheets("Sheet2")
Set ms = Sheets("Sheet1")
ms.Range("B2:B" & ms.Rows.Count).ClearContents
ms.Range("A2:B" & ms.Rows.Count).Font.Color = 0
Set rng = ms.Range("A2:A" & ms.Cells(ms.Rows.Count, 1).End(xlUp).Row)
For Each sFind In rng
'get first character of url
s = Left(sFind, 1)
'resort to column aa if not a a to z
If Asc(UCase(s)) < 65 Or Asc(UCase(s)) > 90 Then s = "AA"
'only look in appropriate column
Set rFind = ws.Columns(s).Find(sFind, , xlValues, xlPart, xlByRows, xlPrevious)
If Not rFind Is Nothing Then
'only look once and save that cell ref
sFind.Offset(, 1) = rFind.Address
sFind.Font.Color = -16776961
Else
'if not found put default string
sFind.Offset(, 1) = "No Found"
sFind.Offset(, 1).Font.Color = -16776961
End If
Next
Set ms = Nothing
Set ws = Nothing
Set rng = Nothing
Set rFind = Nothing
'enable calculation
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
End Sub
Non VBA - Tested on Excel 2010:
=IFERROR(VLOOKUP(A2, INDIRECT("Sheet2!" & IF(OR(CODE(UPPER(LEFT(A2, 1)))<65,
CODE(UPPER(LEFT(A2, 1)))>90), "AA:AA", LEFT(A2, 1)&":"& LEFT(A2, 1))), 1, FALSE),
"Not Found")