Find and copy cells from adjacent worksheet to current worksheet - function

I need to create a macro (or function) to copy cells from an adjacent worksheet to the current worksheet if they meet certain criteria.
Below is the worksheet adjacent to the current worksheet that contains the Owner, Ticket, and Comments fields. I need to copy those fields to the appropriate application name and object (Concatenated as a unique ID) in the current worksheet.
Below is the current worksheet that I need to copy the above fields to. Notice that the applications are not listed in the same order. This will be the case as I never know which order the data will be in or if the same data will even be in the new worksheet.
So far I have tried this function:
=IF(INDIRECT(NextSheetName()&"!A3")&INDIRECT(NextSheetName()&"!B3") = A3&B3, INDIRECT(NextSheetName()&"!D3"), "0")
Which will work only in the case that the worksheets have the same data in the same order.
Does anyone have any idea how this can be done?

If you want to do this using VBA, try the following. The code copies matching rows from the source worksheet to the target worksheet and records the matching row on the source to the target, in case you find that useful. I named my sheets "Source" and "Target" and am assuming that you are wanting to match on the concatenation of columns A and B.
The number of rows in your source and target don't matter, nor does the order in which the matches appear.
I wrote two different versions. The first works, but I'm not crazy about it because it loops through the source range looking for a match for each value in the target. The second version uses a dictionary that is built once. Matching search terms is then done without having to loop through a range. Note that to use the dictionary, you'll need a reference to the Microsoft Scripting Runtime.
First Version: (functional, but requires multiple loops)
Sub GetTwoColumnMatches()
Dim wsrc As Worksheet
Dim wTgt As Worksheet
Dim rng As Range
Dim cell As Range
Dim lLastTargetRow As Long
Dim lMatchedRow As Long
Dim sConcat As String
Set wsrc = Sheets("Source")
Set wTgt = Sheets("Target")
lLastTargetRow = wTgt.Range("A" & wTgt.Rows.Count).End(xlUp).Row
Set rng = wTgt.Range("a2:a" & lLastTargetRow)
For Each cell In rng
sConcat = cell & cell.Offset(, 1)
lMatchedRow = Matches(sConcat)
If lMatchedRow <> 0 Then
wTgt.Range("a" & cell.Row & ":e" & cell.Row).Value = _
wsrc.Range("a" & lMatchedRow & ":e" & lMatchedRow).Value
wTgt.Range("f" & cell.Row) = lMatchedRow
End If
Next
End Sub
Function Matches(SearchFor As String) As Long
Dim wsrc As Worksheet
Dim rng As Range
Dim cell As Range
Dim lLastSourceRow As Long
Dim lSourceRow As Long
Set wsrc = Sheets("Source")
lLastSourceRow = wsrc.Range("a" & wsrc.Rows.Count).End(xlUp).Row
Set rng = wsrc.Range("a2:a" & lLastSourceRow)
Matches = 0
For Each cell In rng
If cell & cell.Offset(, 1) = SearchFor Then
Matches = cell.Row
Exit For
End If
Next
End Function
Second Version: (optimized, requires reference to Microsoft Scripting Runtime)
Sub GetTwoColumnMatches()
Dim wsrc As Worksheet
Dim wTgt As Worksheet
Dim rng As Range
Dim cell As Range
Dim srcRng As Range
Dim srcCell As Range
Dim lLastTargetRow As Long
Dim lLastSourceRow As Long
Dim lMatchedRow As Long
Dim lSourceRow As Long
Dim sConcat As String
Dim dict As Dictionary
Set wsrc = Sheets("Source")
Set wTgt = Sheets("Target")
lLastTargetRow = wTgt.Range("A" & wTgt.Rows.Count).End(xlUp).Row
Set wsrc = Sheets("Source")
lLastSourceRow = wsrc.Range("a" & wsrc.Rows.Count).End(xlUp).Row
'Create the dictionary
Set dict = New Dictionary
Set srcRng = wsrc.Range("a2:b" & lLastSourceRow)
For Each srcCell In srcRng
sConcat = srcCell & srcCell.Offset(, 1)
If Len(sConcat) > 0 Then dict.Add sConcat, srcCell.Row
Next
Set rng = wTgt.Range("a2:a" & lLastTargetRow)
For Each cell In rng
sConcat = cell & cell.Offset(, 1)
lMatchedRow = dict.Item(sConcat)
If lMatchedRow <> 0 Then
wTgt.Range("a" & cell.Row & ":e" & cell.Row).Value = _
wsrc.Range("a" & lMatchedRow & ":e" & lMatchedRow).Value
wTgt.Range("f" & cell.Row) = lMatchedRow
End If
Next
End Sub
Here's what your references will look like once you've correctly selected the Microsoft Scripting Runtime:

Related

How to hide columns in dynamic subranges before converttohtml in emailbody in Outlook?

I am doing a macro that is formatting a data base into a table, and then select ranges from this table in order to send to different persons depending of the range.
But depending of the range sometimes I can have several column empty, I would like to add in my loop that when creating the temporary workbook, to copy paste my subtable that I wanna send, a function or a part that check if the column is empty (I have headers) and if it's the case, hide the columns concerned only for this range and then convert to HTML in my body email the range without my empty column now hidden and after the loop keeps going through my whole table.
Thanks to a previous post, my VBA code is running smoothly but as soon as I add the part which is supposed to hide column, it's not working anymore, I guess, that I am not adding it in the right place but I don't know,
I tried to add it, just after RangeToEmail and in the function that is creating the tempWB, RangetoHTML but it's not working. (see both codes after)
The code I used on a static range and which is working, to hide the column is
Dim iFirstCol As Integer, iLastCol As Integer, i As Integer`
'variables to hold the first and last column numbers
iFirstCol = Range("A2").Column
iLastCol = Range("W2").Column
LastRow = Range(Range("A2"), Range("A2").End(xlDown))
'count backwards through columns
For i = iLastCol To iFirstCol Step -1
'if all cells are blank, hide the column
If WorksheetFunction.CountA(Range(Cells(1, i), Cells(LastRow, i))) = 0 Then
Columns(i).EntireColumn.Hidden = True
End If
Next i
and here is the code I use to go from my table to the different subtable and then through TemporaryWB convert to html in my email body
Option Explicit
Sub GetNames()
Dim NameArray() As String
Dim NameRange As Range
Dim C As Range
Dim Counter As Integer
Dim NameFilter As Variant
Dim RangeToEmail As Range
Dim EmailAddress() As String
'Email Stuff
Dim objOutlook As Object
Set objOutlook = CreateObject("Outlook.application")
Dim objEmail As Object
Set NameRange = Range(Range("H2"), Range("H2").End(xlDown))
ReDim NameArray(1 To Range(Range("H2"), Range("H2").End(xlDown)).Rows.Count) ReDim EmailAddress(1 To Range(Range("H2"), Range("H2").End(xlDown)).Rows.Count)
Counter = 0
For Each C In NameRange
Counter = Counter + 1
NameArray(Counter) = C.Value
EmailAddress(Counter) = C.Offset(, 3)
Next
NameArray = ArrayRemoveDups(NameArray)
EmailAddress = ArrayRemoveDups(EmailAddress)
Counter = 0
For Each NameFilter In NameArray
Counter = Counter + 1
ActiveSheet.Range("A1").AutoFilter Field:=8, Criteria1:=NameFilter Set RangeToEmail = ActiveSheet.ListObjects("DataTable").Range
Set objEmail = objOutlook.CreateItem(olMailItem)
With objEmail .To = EmailAddress(Counter)
.Subject = "TestSubject"
.HTMLBody = "Hello, <br><br>Please see the latest report:<br><br>" & RangetoHTML(RangeToEmail)
.Display
End With
Set objEmail = Nothing
Next
ActiveSheet.Range("A1").AutoFilter
End Sub
Function ArrayRemoveDups(MyArray As Variant) As Variant
Dim nFirst As Long, nLast As Long, i As Long
Dim item As String
Dim arrTemp() As String
Dim Coll As New Collection
'Get First and Last Array Positions
nFirst = LBound(MyArray)
nLast = UBound(MyArray)
ReDim arrTemp(nFirst To nLast)
'Convert Array to String
For i = nFirst To nLast
arrTemp(i) = CStr(MyArray(i))
Next i
'Populate Temporary Collection
On Error Resume Next
For i = nFirst To nLast
Coll.Add arrTemp(i), arrTemp(i)
Next i
Err.Clear
On Error GoTo 0
'Resize Array
nLast = Coll.Count + nFirst - 1
ReDim arrTemp(nFirst To nLast) '
Populate Array
For i = nFirst To nLast
arrTemp(i) = Coll(i - nFirst + 1)
Next i
'Output Array
ArrayRemoveDups = arrTemp
End Function
Function RangetoHTML(rng As Range)
Dim fso As Object
Dim ts As Object
Dim TempFile As String
Dim TempWB As Workbook
TempFile = Environ$("temp") & "/" & Format(Now, "dd-mm-yy h-mm-ss") & ".htm"
' Copy the range and create a workbook to receive the data.
rng.Copy
Set TempWB = Workbooks.Add(1)
With TempWB.Sheets(1)
.Cells(1).PasteSpecial Paste:=8
.Cells(1).PasteSpecial xlPasteValues, , False, False
.Cells(1).PasteSpecial xlPasteFormats, , False, False
.Cells(1).Select
Application.CutCopyMode = False
On Error Resume Next
.DrawingObjects.Visible = True
.DrawingObjects.Delete
On Error GoTo 0
End With
' Publish the sheet to an .htm file.
With TempWB.PublishObjects.Add( _ SourceType:=xlSourceRange, _ Filename:=TempFile, _ Sheet:=TempWB.Sheets(1).Name, _ Source:=TempWB.Sheets(1).UsedRange.Address, _ HtmlType:=xlHtmlStatic)
.Publish (True)
End With
' Read all data from the .htm file into the RangetoHTML subroutine.
Set fso = CreateObject("Scripting.FileSystemObject")
Set ts = fso.GetFile(TempFile).OpenAsTextStream(1, -2)
RangetoHTML = ts.ReadAll
ts.Close RangetoHTML = Replace(RangetoHTML, "align=center x:publishsource=", _ "align=left x:publishsource=")
' Close TempWB. TempWB.Close savechanges:=False
' Delete the htm file.
Kill TempFile
Set ts = Nothing
Set fso = Nothing
Set TempWB = Nothing
End Function
First LastRow is not declared as variable properly and therfore you didn't see that
LastRow = Range(Range("A2"), Range("A2").End(xlDown))
is actually writing an array of values into LastRow. Actually your first code cannot work properly. Make sure you use Option Explicit (I recommend always to activate Option Explicit: In the VBA editor go to Tools › Options › Require Variable Declaration).
The issue is probably if your empty columns have headers too then
WorksheetFunction.CountA(Range(Cells(1, i), Cells(LastRow, i)))
will never be 0 because you included your header row 1 Cells(1, i) in your range. So if you want to exclude the header you need to change it to start with row 2 like Cells(2, i).
Finally all this code applies to ActiveSheet which is not very reliable because the active sheet can change by a single mouse click. If you can specify the worksheet precisely by a name, do so. If it really has to run on multiple sheets (so you really want to use the active one) at least make sure the active sheet does not change during the code excecutes by reading it only once into a variable Set ws = ThisWorkbook.ActiveSheet.
I would use
Dim ws As Worksheet
Set ws = ThisWorkbook.Worksheets("Sheet1") 'name your sheet here!
'or if it really is the active sheet do
'Set ws = ThisWorkbook.ActiveSheet 'and make sure you only use `ws` from now!
'variables to hold the first and last column numbers
Dim iFirstCol As Long
iFirstCol = ws.Columns("A").Column
Dim iLastCol As Long
iLastCol = ws.Columns("W").Column
Dim LastRow As Long
LastRow = ws.Cells(ws.Rows.Count, "A").End(xlDown).Row
'count backwards through columns
Dim i As Long
For i = iLastCol To iFirstCol Step -1
'if all cells are blank, hide the column
If Application.WorksheetFunction.CountA(ws.Range(ws.Cells(2, i), ws.Cells(LastRow, i))) = 0 Then
ws.Columns(i).EntireColumn.Hidden = True
End If
Next i
Apply the same to the rest of your code to make it more reliable.

Parsing Html in VBA using a query get request

I am using someone else's code because this is an old file other people are using, I want to update it to make it more efficient but I need a little help. Below is the vba operation. What I need is it to get the information but delete everything but a certain word which changes every time the operation is run. I could use regex and objRE.Pattern = "|" but the word changes depending on the status.
HTML:
<span onmouseover="ShowText('Message','blahblah'); return true;"
onmouseout="HideText('Message'); return true;"
href="javascript:ShowText('Message')">---(PSA)---</span>
</font><a href='?srn=numbers12131131'target='_self'><font color='#6666FF'
size='3'>numbers123232343</font></a><font size='3'>----Installed----MUM
Indication:In Scope-<font color='#00CC00'>PASS WITH WARNING</font>--- (20181018)
</td><tr></table> </b><br>
<table class="OrderForm" width="1000"> '
I just want the Installed status in my excel sheet.
VBA code that needs work:
Sub GetComment()
Dim book As Workbook
Dim sheet As Worksheet
Dim row As Integer
Dim SRN As String
Dim whttp As Object
Set book = ThisWorkbook
Set sheet = book.Worksheets("CMT Data")
Set whttp = CreateObject("WinHTTP.WinHTTPrequest.5.1")
row = 2
SRN = sheet.Cells(row, 1)
Do While SRN <> ""
Debug.Print SRN
whttp.Open "GET", "www.websitedatgoeshere.com" & SRN, False
whttp.SetRequestHeader "Cookie", "mycookiefromwebsite;"
whttp.send
Debug.Print whttp.responseText
sheet.Cells(row, 2) = whttp.responseText
row = row + 1
SRN = sheet.Cells(row, 1)
Loop
Set whttp = Nothing
End Sub
This is based on if, and only if, the word is always between "----" and "----", and that it is the first occurrence in the response. If not the first you can adjust the index 1 as required.
Debug.Print Split(Split(whttp.responseText, "----")(1), "----")(0)
sheet.Cells(row, 2) = Split(Split(whttp.responseText, "----")(1), "----")(0)

Issue With Looping/Variables Through HTML Table Elements

I have this project I'm working on which involves tracking the order statuses of individual shipments from a list of URLs on an Excel sheet. Currently, the code is able to loop through the URLs fine and extract information from them, but when I try to add exceptions and variables to the loop for each different URL, I get a type mismatch. Currently, there are 3 variables I need in order to extract the HTML information from the HTMLCollection/Table. What I'm trying to do here is:
Cycle through each URL (inserts URL in IE and keeps going without opening new tabs)
Obtain the status of the item for each URL from the HTML element
FedEx: Delivered (td class="status")
UPS: Delivered (id="tt_spStatus")
USPS: Arrived at USPS Facility (class= "info-text first)
My code:
Sub TrackingDeliveryStatusUpdate()
Application.Calculation = xlCalculationManual
Application.ScreenUpdating = False
Application.EnableEvents = True
Dim rngURL As Range
Dim wb1 As Workbook, ws1 As Worksheet, ws2 As Worksheet
Dim MyURL As String
Dim Rows As Long, links As Variant, IE As InternetExplorer, link As Variant
Dim i As Long
Dim sID As String
Dim rngLinks As Range, rngLink As Range
Dim filterRange As Range
Dim copyRange As Range
'Dim doc As Object
Set wb1 = Application.Workbooks.Open("\\S51\Store51\Employee Folders\Jason\TrackingDeliveryStatus.xls")
Set ws1 = wb1.Worksheets("TrackingDeliveryStatusResults")
'Set rngURL = ws1.Range("C2:C" & lastRow)
'Arr = rngURL
Set IE = New InternetExplorer
Rows = ws1.Cells(ws1.Rows.Count, "A").End(xlUp).Row
Set rngLinks = ws1.Range("C2:C" & Rows)
'Loop through each used cell in column C on sheet URLs
'For Each rngURL In Worksheets("TrackingDeliveryStatusResults").Range("C2", Worksheets("TrackingDeliveryStatusResults").Range("C" & Rows.Count).End(xlUp))
'Set HTMLDoc = IE.Document
With IE
.Visible = True
i = 2
For Each rngLink In rngLinks
.Navigate (rngLink)
While .Busy Or .ReadyState <> 4: DoEvents: Wend
'If InStr(1, URL.Value, "fedex") Then sID = "status"
Dim doc As Object
Set doc = IE.Document
'I know this line right below is a big mess. Not sure what to do here.
If InStr(1, rngLink.Value2, "ups") Then
sID = "tt_spStatus"
'If InStr(3, URL.Value, "usps") Then sID = "info-text first"
ws1.Range("D" & i).Value = doc.getElementById(sID).Items(0).Value
'ws1.Range("D" & i).Value = .Document.body.innerText
End If
Next rngLink
i = i + 1
End With
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
Application.EnableEvents = True
End Sub
I figured this would work, but apparently it doesn't with referencing URLs from an Excel spreadsheet.
You should dim Link and Links as range and change:
links = ws1.Range("C2:C" & Rows)
to:
Set Links = ws1.Range("C2:C" & Rows)
You are trying to make a Variant equal a Range which can't happen. But once you set Links to the range of cells, then when you're looping through each cell reference the URL with link.Value2.
The reason is not working is because you have several concepts mixed up in your statements. I have refactored the relevant parts of the code and made some suggestions on variable type and name changes
Dim rngLinks as Range, rngLink as Range
Set rng Links = ws1.Range("C2:C" & Rows)
For each rngLink in rngLinks
Just because here you are looping through cells in a worksheet range.
Then inside the For each rngLink in rngLinks statement, after the statement where the code waits for IE to load.
Dim doc as Object
Set doc = .document
If InStr(1, rngLink.Value2, "ups") Then
sID = "tt_spStatus"
ws1.Range("D" & I).Value = doc.getElementById(sID).Items(0).Value
End If
You can also delete the For Each TDelement In sID block. It is not needed.

Parsing data into access from network shared excel workbook

I am parsing a worksheet into Access using the following code:
Sub LoadRates(ByRef TimesheetFile As Excel.Workbook)
On Error GoTo LoadDataCollection_Error
Dim i As Integer
Dim LastRow As Integer
Dim shRates As Excel.Worksheet
Set shRates = TimesheetFile.Worksheets("Rates")
shRates.ShowAllData
LastRow = shRates.Cells(shRates.Rows.Count, 1).End(xlUp).Row
shRates.Cells(1, 4).value = "Current"
Dim db As DAO.Database
Set db = CurrentDb
Dim strSQL As String
Dim dbWb As String
dbWb = "[Excel 12.0;HDR=YES;IMEX=1;Database=" & TimesheetFile.FullName & "].[Rates$A1:i" & LastRow & "]"
strSQL = "SELECT A.[Entity no] AS Entity,Staff AS Name, A.Current as Rates, Company, [2015 BCTC category] AS BCTC_Category,[2015 Rating] As Rating " & _
" INTO fromTimesheet " & _
" FROM " & dbWb & " AS A "
db.Execute strSQL, dbFailOnError
Exit Sub
LoadDataCollection_Error:
... do stuff...
End Sub
my problem is that I have to change the column 4's header to "Current" manually because the title is currently a date value, thus inappropriate for SQL to pick up
shRates.Cells(1, 4).value = "Current"
when I execute this code. sometimes it returns an error message saying it cannot find the field A.Current , and other times it would be able to find it. Is this caused by the workbook being set to Shared?
Any help would be appreciated.
Upon testing, I found out that this error has to do with the excel workbook being "Shared". Once I made the workbook exclusive, the code was able to pick up me editing the row value.
This leads me to believe that when I instantiate an excel workbook object that is being shared. It should be treated as read-only.
Hopefully this is helpful for others.
Yes, your column header is not being effectively saved and connection is using last saved instance.
Consider manually saving it after you make your change. Also, be sure you have write access to save workbook and not in shared/read-only (or alternatively save a different temp workbook for import):
shRates.Cells(1, 4).value = "Current"
TimesheetFile.Save
But also consider using DoCmd.TransferSpreadshet method instead of worksheet connection (which can facilitate "shared" process). Usually one uses a true database for connection and not flatfiles like spreadsheets.
Sub LoadRates(ByRef TimesheetFile As Excel.Workbook)
On Error GoTo LoadDataCollection_Error
Dim i As Integer, LastRow As Integer
Dim shRates As Excel.Worksheet
Set shRates = TimesheetFile.Worksheets("Rates")
shRates.ShowAllData
LastRow = shRates.Cells(shRates.Rows.Count, 1).End(xlUp).Row
shRates.Cells(1, 4).value = "Current"
TimesheetFile.Close True ' SAVES AND CLOSES
DoCmd.TransferSpreadsheet acImport, "fromTimesheet", _
TimesheetFile.FullName, True, "Rates!"
Exit Sub
LoadDataCollection_Error:
... do stuff...
End Sub

How to create individual HTML publish pages using loop through excel spreadsheet

I am trying to create individual HTML pages using a loop function on an excel spreadsheet. I had been manually publishing each page but I have thousands of entries, so I need an automated method using macro. I recorded a macro with the steps I use through the manual approach shown below:
Sub HTMLexport()
Columns("A:W").Select
With ActiveWorkbook.PublishObjects.Add(xlSourceRange, _
"C:\Users\<user_name>\Desktop\Excel2HTML\Articles\1045_VSE.htm", _
"Sheet1", "$A:$W", xlHtmlStatic, _
"FileName_10067", "")
.Publish (True)
End With
Columns("W:W").Select
Selection.EntireColumn.Hidden = True
End Sub
Ultimately what I want is to be able to have Column A and the next column selected (ex. B, C, H, Etc), then have those two published into an HTML page. The name of the file I would like to be based on a cell reference. Ex. Cell W3 would have a value of 1045, and the file name be saved as 1045_VSE.htm, where _VSE is constant through the loop process. That way, each new HTML page name would increment based on the cell reference. Once the HTML page is saved, hide the column and move to the next one, rinse and repeat. Any help with this would be amazing. Thanks in advance.
This should be fairly straightforward to put inside a loop.
Here is an example. I assume that the filename will come from the first row/second column in the sub-range, you can easily modify this, or ask me how and I can revise. I also assume that the Div ID ("FileName_100067") is constant. Again, this can easily be modified if needed.
Sub HTMLinLoop()
Dim wb As Workbook: Set wb = ActiveWorkbook
Dim ws As Worksheet: Set ws = ActiveSheet
Dim rng As Range '## The full range including all columns'
Dim subRng As Range '## a variable to contain each publishObjects range'
Dim pObj As PublishObject '## A variable to contain each publishObject as we create it.'
Dim p As Long '## use this integer to iterate over the columns in rng'
Dim fileName As String '## represents just the file name to export'
Dim fullFileName As String '## the full file path for each export'
Dim divName As String '## variable for the DivID argument, assume static for now'
Set rng = ws.Range("A3:W30") '## modify as needed'
For p = 1 To rng.Columns.Count
'Identify the sub-range to use for this HTML export:'
' this will create ranges like "A:B", then "A:C", then "A:D", etc.'
Set subRng = Range(rng.Columns(1).Address, rng.Columns(p + 1).Address)
'Create the filename:'
'## modify as needed, probably using a range offset.'
fileName = subRng.Cells(1, 2).Value & "_VSE.htm"
'Concatenate the filename & path:'
'## modify as needed.'
exportFileName = "C:\Users\" & Environ("Username") & "\Desktop\" & fileName
'Create hte DIV ID:'
divName = "FileName_10067" '## modify as needed, probably using a range offset.'
'## Now, create the publish object with the above arguments:'
Set pObj = wb.PublishObjects.Add( _
SourceType:=xlSourceRange, _
fileName:=exportFileName, _
Sheet:=ws.Name, _
Source:=subRng.Address, _
HtmlType:=xlHtmlStatic, _
DivID:=divName, _
Title:="")
'## Finally, publish it!'
pObj.Publish
'## Hide the last column:'
rng.Columns(p+1).EntireColumn.Hidden = True
Next
End Sub