The whole code is trying to get historical data from Yahoo Finance using VBA. Everything works pretty fine except the code whereby I try to get the company's name using from the Yahoo site.
This first piece of code is just to chceck that there are no mistakes in the definition in variables or whatever.
Enum READYSTATE
READYSTATE_UNINITIALIZED = 0
READYSTATE_LOADING = 1
READYSTATE_LOADED = 2
READYSTATE_INTERACTIVE = 3
READYSTATE_COMPLETE = 4
End Enum
Sub GetData()
Dim datasheet As Worksheet
Dim EndDate As Date
Dim StartDate As Date
Dim symbol As String
Dim qurl As String
Dim nQuery As Name
Dim LastRow As Integer
Dim ohtml As HTMLText
On Error GoTo error_getdata
Application.ScreenUpdating = False
Application.DisplayAlerts = False
Application.Calculation = xlCalculationManual
Set datasheet = ActiveSheet
StartDate = datasheet.Range("startDate").Value
EndDate = datasheet.Range("endDate").Value
symbol = datasheet.Range("ticker").Value
symbol = UCase(symbol)
'Download data from Yahoo Finance'
Sheets("Home").Activate
Sheets(symbol).Range("a1").CurrentRegion.ClearContents
qurl = "http://ichart.finance.yahoo.com/table.csv?s=" & symbol
qurl = qurl & "&a=" & Month(StartDate) - 1 & "&b=" & Day(StartDate) & _
"&c=" & Year(StartDate) & "&d=" & Month(EndDate) - 1 & "&e=" & _
Day(EndDate) & "&f=" & Year(EndDate) & "&g=" & Sheets(symbol).Range("a1") & "&q=q&y=0&z=" & _
symbol & "&x=.csv"
eurl = "https://finance.yahoo.com/quote/" & symbol & "?ltr=2"
Here is where the problem spots. I try to scrap the html of the site looking for the company's name. If I look at the html code of the website, I find that the company's name is labeled as reactid="239". I guess that what I have to do is to use getelementsbyID("239") but I am not sure about that.
'''''
Dim objIe As Object
Set objIe = CreateObject("InternetExplorer.Application")
objIe.Visible = False
objIe.navigate eurl
Application.StatusBar = "Looking for information in Yahoo Finance"
While (objIe.Busy Or objIe.READYSTATE <> 4): DoEvents: Wend
Set xobj = objIe.querySelectorAll("[reactid=239]")
Debug.Print xobj.innerText
Set xobj = Nothing
objIe.Quit
Set objIe = Nothing
Application.StatusBar = ""
'Sort the existence of a ticker in our sheet and create a new one '
Dim worksh As Integer
Dim worksheetexists As Boolean
Dim x As Integer
worksh = Application.Sheets.Count
worksheetexists = False
For x = 1 To worksh
If Worksheets(x).Name = symbol Then
worksheetexists = True
Sheets(symbol).Delete
ActiveWorkbook.Sheets.Add(after:=Worksheets(Worksheets.Count)).Name = symbol
Exit For
End If
Next x
If worksheetexists = False Then
ActiveWorkbook.Sheets.Add(after:=Worksheets(Worksheets.Count)).Name = symbol
End If
' Load data '
QueryQuote:
With Sheets(symbol).QueryTables.Add(Connection:="URL;" & qurl, Destination:=Sheets(symbol).Range("a1"))
.BackgroundQuery = True
.TablesOnlyFromHTML = False
.Refresh BackgroundQuery:=False
.SaveData = True
End With
Sheets(symbol).Range("a1").CurrentRegion.TextToColumns Destination:=Sheets(symbol).Range("a1"), DataType:=xlDelimited, _
TextQualifier:=xlDoubleQuote, ConsecutiveDelimiter:=False, Tab:=True, _
Semicolon:=False, Comma:=True, Space:=False, other:=False
Sheets(symbol).Columns("A:G").ColumnWidth = 12
'Sort data'
LastRow = Sheets(symbol).UsedRange.Row - 2 + Sheets(symbol).UsedRange.Rows.Count
Sheets(symbol).Sort.SortFields.Add Key:=Range("A2"), _
SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
With Sheets(symbol).Sort
.SetRange Range("A1:G" & LastRow)
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
.SortFields.Clear
End With
Exit Sub
error_getdata:
MsgBox ("Fatal error. Please insert a valid sticker for the stock")
End Sub
I heed that this could not be the most efficient way to get what I want. First I want to learn how to get it done and then I will take on the efficiency of the program.
Edit: Using some answers, I edited a bit the code, it still shows up an error (error 438) on the line:
Set xobj = objIe.querySelectorAll("[reactid=239]")
I'd look into using http://www.w3schools.com/jsref/met_document_queryselectorall.asp
which can allow selection of nodes using CSS selector syntax and there is a reference for this syntax at http://www.w3schools.com/cssref/css_selectors.asp
So perhaps something along the lines of
document.querySelectorAll("[reactid=239]")
Incidentally, you can browse the library if you use a Tools Reference to
Microsoft HTML Object Library
Related
I have an excel sheet where the columns and rows of the table are changed from time to time. The affected vba script, however, uses fixed values for rows and columns. How can I find the columns and rows if they change? The name of the columns is not changed, but only the location in the sheet.
I have to upadte my method manually everytime. (Like you see in the code example)
Hello Siddharth, thank you for your detailed description. Unfortunately I do not have experience with VBA, so I can not support the integration of your code. I suspect that the return variable does not match the specified method. Here is my VBA script that needs to be extended. I hope you can help me there =)
Option Explicit
Public Sub moduleStatus()
Dim iQZeMax As Integer
Dim iQZe As Integer
Dim iZZe As Integer
Dim iQSp As Integer
Dim shtSPR_R As Worksheet, shtAdd As Worksheet
Dim rng_2_check As Range
Dim lstLong(3) As String
lstLong(0) = "Initiated"
lstLong(1) = "Review ready"
lstLong(2) = "Reviewed"
Dim lstShort(2) As String
lstShort(0) = "Initiated"
lstShort(1) = "Review ready"
Application.EnableEvents = False
Application.ScreenUpdating = False
Set shtSPR_R = ThisWorkbook.Sheets("Report")
Set shtAdd = ThisWorkbook.Sheets("Add")
'Unprotect
shtSPR_R.Unprotect
'Clear old Data
'''shtSPR_R.Range("AB11:AB10000").ClearContents
'Status
iQSp = 28
'''iQZe = 11
'max row is determined by MA
For iQZeMax = 10010 To 1 Step -1
If shtSPR_R.Range("A" & iQZeMax).Value <> "" Or shtSPR_R.Range("B" & iQZeMax).Value <> "" Then Exit For
Next
shtSPR_R.Range("AC11:AD10010").Clear
shtSPR_R.Range("A1").FormatConditions(1).ModifyAppliesToRange Range:=shtSPR_R.Range("A1:AE10010")
For iQZe = 11 To iQZeMax
' If Application.WorksheetFunction.CountIfs(shtSPR_R.Range("A" & iQZe & ":AB" & iQZe), "") = iQSp Then
' Exit For
' End If
'Case Initiated
If shtSPR_R.Range("AB" & iQZe).Value = "" Then
shtSPR_R.Range("AB" & iQZe).Validation.Delete
shtSPR_R.Range("AB" & iQZe).Value = "Initiated"
shtSPR_R.Cells(iQZe, iQSp).Validation.Add _
Type:=xlValidateList, _
AlertStyle:=xlValidAlertStop, _
Operator:=xlBetween, _
Formula1:=Join(lstShort, ",")
End If
If Application.WorksheetFunction.CountIfs(shtSPR_R.Range("A9:AB9"), "Required", shtSPR_R.Range("A" & iQZe & ":AB" & iQZe), "") <> 0 Then
shtSPR_R.Range("AB" & iQZe).Validation.Delete
shtSPR_R.Range("AB" & iQZe).Validation.Add _
Type:=xlValidateList, _
AlertStyle:=xlValidAlertStop, _
Operator:=xlBetween, _
Formula1:=Join(lstShort, ",")
Else
shtSPR_R.Range("AB" & iQZe).Validation.Delete
shtSPR_R.Cells(iQZe, iQSp).Validation.Add _
Type:=xlValidateList, _
AlertStyle:=xlValidAlertStop, _
Operator:=xlBetween, _
Formula1:=Join(lstLong, ",")
End If
'shtSPR_R.Range("AC" & iQZe).FormulaArray = "=IFERROR(INDEX(general_report!R3C5:R10000C5,MATCH(RC[-27]&RC[-26]&RC[-22],general_report!R3C8:R10000C8&general_report!R3C2:R10000C2&general_report!R3C9:R10000C9,0)),""tbd."")"
'shtSPR_R.Range("AD" & iQZe).FormulaArray = "=IFERROR(INDEX(general_report!R3C6:R10000C6,MATCH(RC[-28]&RC[-27]&RC[-23],general_report!R3C8:R10000C8&general_report!R3C2:R10000C2&general_report!R3C9:R10000C9,0)),""tbd."")"
shtSPR_R.Range("AC" & iQZe).FormulaArray = "=IFERROR(INDEX(general_report!R4C6:R10000C6,MATCH(RC[-27]&RC[-26]&RC[-22],general_report!R4C9:R10000C9&general_report!R4C2:R10000C2&general_report!R4C10:R10000C10,0)),""tbd."")"
shtSPR_R.Range("AD" & iQZe).FormulaArray = "=IFERROR(INDEX(general_report!R4C7:R10000C7,MATCH(RC[-28]&RC[-27]&RC[-23],general_report!R4C9:R10000C9&general_report!R4C2:R10000C2&general_report!R4C10:R10000C10,0)),""tbd."")"
If shtSPR_R.Range("AB" & iQZe).Value = "Exported" Then
shtSPR_R.Range("A" & iQZe & ":AA" & iQZe).Locked = True
Else
shtSPR_R.Range("A" & iQZe & ":AA" & iQZe).Locked = False
End If
If shtSPR_R.Range("AE" & iQZe).Value = "" Then
shtAdd.Range("rngSPR_ID_Cnt").Value = shtAdd.Range("rngSPR_ID_Cnt").Value + 1
shtSPR_R.Range("AE" & iQZe).Value = shtSPR_R.Range("L" & iQZe).Value & "-" & Right("00000" & shtAdd.Range("rngSPR_ID_Cnt").Value, 5)
End If
Next iQZe
'Protect
shtSPR_R.Protect "", DrawingObjects:=False, Contents:=True, Scenarios:=True _
, AllowFormattingCells:=True, AllowFormattingColumns:=True, _
AllowFormattingRows:=True, AllowFiltering:=True
Application.ScreenUpdating = True
Application.EnableEvents = True
MsgBox "Done!"
End Sub
Here is another way to do it.
What you are actually trying to get is the R4C6:R10000C6 part of the formula. So what you can do is use a common sub to get the address and then create your own formula string. I am using .Find to locate the column header. To read more about .Find, you can see .Find and .FindNext
Here is an example for Linked Issues.
Option Explicit
Sub Sample()
Debug.Print GetAddress("Linked Issues")
End Sub
Private Function GetAddress(ColHeader As String) As String
Dim HeaderRow As Long, HeaderCol As Long
Dim rngAddress As String: rngAddress = "Not Found"
Dim aCell As Range
Dim ws As Worksheet
'~~> Change this to the relevant sheet
Set ws = Sheet1
With ws
Set aCell = .Cells.Find(What:=ColHeader, LookIn:=xlValues, _
LookAt:=xlWhole, SearchOrder:=xlByRows, SearchDirection:=xlNext, _
MatchCase:=False, SearchFormat:=False)
If Not aCell Is Nothing Then
HeaderRow = aCell.Row
HeaderCol = aCell.Column
rngAddress = "R" & (HeaderRow + 1) & "C" & HeaderCol & _
":R10000C" & HeaderCol
End If
End With
GetAddress = rngAddress
End Function
CAUTION: You may get false positives if the column name is repeated elsewhere. I have used LookAt:=xlWhole to minimize that but you still need to be careful.
Screenshot:
When you run the code you will get R4C3:R10000C3
Also if you want to avoid the hardcoding of 10000, then find the last row. For that you can see THIS
Create a new spreadsheet, let's say "keys"
The 1st column of which will be "columns" and the 3rd of which will be rows,
then you add a MATCH function, that gives you the location of the row and column,
so what you'll need to do is link the VBA to keys sheet, and grab the location from there
The formula for each column:
IFERROR(ADDRESS(1,MATCH($A2,'1'!$A$1:$A$1000,0)),"missing")
IFERROR(ADDRESS(MATCH($C2,'1'!$A$2:$BA$2,0),2),"missing")
And lastly, connect the formulas' results witho your VBA:
shtSPR_R.Range("keys!B2").FormulaArray = ...
Hope that helps
I've been trying to make this work. It worked fine on my x64 version of office but not the x86 on my colleagues' computers. Can anyone gimme a fix please?
The VBA engine highlighted Range("AV5").AutoFill Destination:=Range("AV5:AV" & NoOfClients) as the cause
Private Sub Check_Cases_Click()
Dim NoOfClients As Long
Application.DisplayAlerts = False
CO_Select = Application.InputBox("Please input the name of caseworker you would like to check on.", "Caseworker Name")
Range("A2").value = CO_Select
Application.ScreenUpdating = False
NoOfClients = Range("C2").value
CO_Name = Range("A2").value
CheckCaseMsg = MsgBox(CO_Name & ", there are " & NoOfClients & " clients under your name." & vbNewLine & vbNewLine & _
"System will now calculate all your active cases and display " & vbNewLine & _
"all the clients for your information." & vbNewLine & vbNewLine & _
"Confirm?", vbYesNo, "Case Checking")
If CheckCaseMsg = vbNo Then
Exit Sub
End If
If CheckCaseMsg = vbYes Then
'Remove the filters if one exists
'=========================================
If ActiveSheet.FilterMode Then
Selection.AutoFilter
End If
Clear
Startup_Formula
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
Range("AV5").AutoFill Destination:=Range("AV5:AV" & NoOfClients)
Application.Calculation = xlCalculationAutomatic
Range("GI_Table[[#All],[Client number]]").Copy
Range("GI_Table[[#All],[Client number]]").PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks:=False, Transpose:=False
Application.ScreenUpdating = True
ActiveSheet.ListObjects("GI_Table").Range.AutoFilter Field:=2, Criteria1:= _
Array("ACTIVE", "INACTIVE", "RENEWED"), Operator:=xlFilterValues
GI_CustomSort
GI_CustomSort
MsgBox "Case Checking Ready", vbInformation, "Ready"
End If
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
Application.DisplayAlerts = True
End Sub
Instead of just Range(".."). you should try
Dim sht as Worksheet
Set sht = Worksheets("Name")
sht.Range("..")
and so on.
You will get the Autofill method of range class failed error if the value of NoOfClients is 5. The end row is the same as the start row. Here is a simple way to reproduce the error.
Sub Sample()
NoOfClients = 5
Range("AV5").AutoFill Destination:=Range("AV5:AV" & NoOfClients)
End Sub
An alternative to .Autofill is to directly write to the range. For ex:
Range("AV5:AV" & NoOfClients).Formula = Range("AV5").Formula
Ok I know what's the problem now, I think.
I limited my scroll area in the Sub for Worksheet_Activate and the value of NoOfClients exceeded the allowed amount!
So I deleted that and I think it's ok now! This is my final code (with the scroll area becoming dynamic now)
Private Sub Check_Cases_Click()
Dim NoOfClients As Long
ActiveSheet.ScrollArea = ""
Application.Calculation = xlCalculationAutomatic
Application.DisplayAlerts = False
CO_Select = Application.InputBox("Please input the name of caseworker you would like to check on.", "Caseworker Name")
Range("A2").value = CO_Select
Application.ScreenUpdating = False
NoOfClients = Range("C2").value
CO_Name = Range("A2").value
CheckCaseMsg = MsgBox(CO_Name & ", there are " & NoOfClients & " clients under your name." & vbNewLine & vbNewLine & _
"System will now calculate all your active cases and display " & vbNewLine & _
"all the clients for your information." & vbNewLine & vbNewLine & _
"Confirm?", vbYesNo, "Case Checking")
If CheckCaseMsg = vbNo Then
Exit Sub
End If
If CheckCaseMsg = vbYes Then
'Remove the filters if one exists
'=========================================
If ActiveSheet.FilterMode Then
Selection.AutoFilter
End If
Clear
Startup_Formula
'Fill down the formula for n times where n= No of Clients of the Caseworker'
'=============================================================================
Dim Sht As Worksheet
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
Set Sht = Worksheets("Grand Info Sheet")
NoOfClients = Range("C2").value
NoOfClientsAdjusted = NoOfClients + 4
Sht.Range("AV5").AutoFill Destination:=Sht.Range("AV5:AV" & NoOfClientsAdjusted)
Application.Calculation = xlCalculationAutomatic
Range("GI_Table[[#All],[Client number]]").Copy
Range("GI_Table[[#All],[Client number]]").PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks:=False, Transpose:=False
Application.ScreenUpdating = True
GI_CustomSort
MsgBox "Case Checking Ready", vbInformation, "Ready"
Range("A1").Select
End If
ActiveSheet.ScrollArea = "A1:AW" & NoOfClientsAdjusted + 5
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
Application.DisplayAlerts = True
End Sub
I have block of text in (A795:O830). It is separated from other pages by Page Break and is always the last page of printed (xls, pdf) document. Sometimes there are a lot of free space on first pages and (A795:O830) could have fitted there. Now it is done by Page Break and is not changeble. In my situation 66 rows can fit on one page.
Is there any macro that can automatically check if there is enough empty space on previous page to fit (A795:O830) and insert it there?
Here is my current macro:
Sub Remove_color()
Dim myRange As Range
Dim cell As Range
Set myRange = Range("A24:O785")
For Each cell In myRange
myRange.Interior.ColorIndex = 0
Next
End Sub
Sub Hide_empty_cells()
Set rr = Range("A24:N832")
For Each cell In rr
cell.Select
If cell.HasFormula = True And cell.Value = "" And cell.EntireRow.Hidden = False Then Rows(cell.Row).EntireRow.Hidden = True
Next cell
End Sub
Sub Save_excel()
Dim iFileName$, iRow&, iCol&, iCell As Range, iArr
iFileName = ThisWorkbook.Path & "\New_folder_" & [D5] & "_" & ".xls"
iArr = Array(1, 3, 4): iCol = UBound(iArr) + 1
Application.ScreenUpdating = False
Application.DisplayAlerts = False
Application.Calculation = xlManual
ThisWorkbook.Sheets(2).Copy
With ActiveWorkbook.ActiveSheet
.Buttons.Delete '.Shapes("Button 1").Delete
.UsedRange.Value = .UsedRange.Value
.SaveAs iFileName, xlExcel8: .Parent.Close
End With
Application.Calculation = xlAutomatic
Application.DisplayAlerts = True
Application.ScreenUpdating = True
End Sub
Sub Save_pdf()
ActiveWorkbook.Sheets(2).Select
ActiveSheet.ExportAsFixedFormat _
Type:=xlTypePDF, _
Filename:=ThisWorkbook.Path & "\New_folder_\" & [D5] & "_" & ".pdf", _
Quality:=xlQualityStandard, _
IncludeDocProperties:=True, _
IgnorePrintAreas:=No, _
OpenAfterPublish:=False
End Sub
Sub doitallplease()
Call Remove_color
Call Hide_empty_cells
Call Save_excel
Call Save_pdf
End Sub
I want to create a macro that copies a sheet called "Week" from my workbook, deletes the first row, adds a new column (farthest to the left), assigns it the header "Department" and assigns it a fixed value. The fixed value should be the name of the CSV file. The name can be found on the front page in cell G6. I don't want the fixed value to be copied all the way down in the first column. I want it to be copied until there isn't any value in any of the columns to the right of the first column. Currently I've tried just comparing it to the second column (column B). I get the message:
Run-time error '424':
Object required
and is referring back to:ยจ
If InStr(1, thiswork.Sheets(ActiveSheet.Name).Range("$B$" & X), "") > 0 Then
This is my code:
Sub Export_pb_uge()
Dim MyPath As String
Dim MyFileName As String
MyPath = "C:mypath1"
MyFileName = Sheets("Front_Page").Range("g6").Value
Application.ScreenUpdating = False
Application.DisplayAlerts = False
If Not Right(MyPath, 1) = "\" Then MyPath = MyPath & "\"
If Not Right(MyFileName, 4) = ".csv" Then MyFileName = MyFileName & ".csv"
Sheets("PB_uge").Visible = True
Sheets("PB_uge").Copy
Rows(1).EntireRow.Delete
With target_sheet
Range("A1").EntireColumn.Insert
Range("A1").Value = "Department"
End With
If ThisWorkbook.Sheets(ActiveSheet.Name).FilterMode Then ThisWorkbook.Sheets(ActiveSheet.Name).ShowAllData
lRow = ThisWorkbook.Sheets(ActiveSheet.Name).Cells(Rows.Count, "B").End(xlUp).Row
For X = 1 To lRow
If InStr(1, thiswork.Sheets(ActiveSheet.Name).Range("$B$" & X), "") > 0 Then
target_sheet.Range("$A$" & X) = ActiveSheet.Name
End If
Next
With ActiveWorkbook
.SaveAs Filename:= _
MyPath & MyFileName, _
FileFormat:=xlCSV, _
CreateBackup:=False, _
Local:=True
.Close False
End With
Sheets("Week").Visible = False
Application.ScreenUpdating = True
Application.DisplayAlerts = True
End Sub
Well spotted gazzz0x2z, however I would also declare and set target_sheet
Dim target_sheet As Worksheet
Set target_sheet = ActiveSheet ' or for example Sheets("sheet1")
With target_sheet
Range("A1").EntireColumn.Insert
Range("A1").Value = "Department"
End With
If ThisWorkbook.Sheets(ActiveSheet.Name).FilterMode Then ThisWorkbook.Sheets (ActiveSheet.Name).ShowAllData
lRow = ThisWorkbook.Sheets(ActiveSheet.Name).Cells(Rows.Count, "B").End(xlUp).Row
For X = 1 To lRow
If InStr(1, ThisWorkbook.Sheets(ActiveSheet.Name).Range("$B$" & X), "") > 0 Then
target_sheet.Range("$A$" & X) = ActiveSheet.Name
End If
Next
Try :
If InStr(1, ThisWorkbook.Sheets(ActiveSheet.Name).Range("$B$" & X), "") > 0 Then
Seems like, for some reason, you've lost 4 letters.
I found the answer to be:
Sub Export_PB_uge()
Dim pb_uge As Worksheet
Dim myPath As String
Dim MyFileName As String
Dim x As Long
Dim wsCSV As Worksheet
myPath = "C:mypath1"
MyFileName = Sheets("Front_Page").Range("g6").Value
Application.ScreenUpdating = False
Application.DisplayAlerts = False
Application.Calculation = xlCalculationManual
Application.EnableEvents = False
If Not Right(myPath, 1) = "\" Then myPath = myPath & "\"
If Not Right(MyFileName, 4) = ".csv" Then MyFileName = MyFileName & ".csv"
With ThisWorkbook.Sheets("PB_uge")
If .FilterMode Then pb_uge.ShowAllData
.Visible = True
.Copy
End With
Set wsCSV = ActiveWorkbook.Sheets(1)
With wsCSV
.Range("A1").EntireRow.Delete
.Range("A1").EntireColumn.Insert
.Range("A1").Value = "Department"
lRow = .Cells(Rows.Count, "C").End(xlUp).Row
.Range("A2:A" & lRow) = ThisWorkbook.Sheets("Front_Page").Range("g6").Value
.Parent.SaveAs Filename:= _
myPath & MyFileName, _
FileFormat:=xlCSV, _
CreateBackup:=False, _
Local:=True
.Parent.Close False
End With
ThisWorkbook.Sheets("PB_uge").Visible = False
Application.EnableEvents = True
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
Application.DisplayAlerts = True
MsgBox "CSV saved at " & myPath & MyFileName, vbInformation
End Sub
I have the below vba macro for merging multiple files. However, when im merging the files, they dont merge in order of how my folder is set up for that path. Could someone tell me how i could get my files to merge in order?
Dim booklist As Workbook
Dim mergeObj As Object, dirObj As Object, filesObj As Object, everyObj As Object
Application.ScreenUpdating = False
Set mergeObj = CreateObject("Scripting.FileSystemObject")
Set dirObj = mergeObj.Getfolder("PATH")
Set filesObj = dirObj.Files
For Each everyObj In filesObj
Set booklist = Workbooks.Open(everyObj)
Range("A1:H27").Copy
ThisWorkbook.Worksheets(1).Activate
Range("A65536").End(xlUp).Offset(2, 0).PasteSpecial
Application.CutCopyMode = False
booklist.Close
Next
Rows("1:1").Select
Selection.Delete Shift:=xlUp
Rows("1:1").Select
Selection.Delete Shift:=xlUp
Range("A1").Select
End Sub
The files will always appear in a random order in your VBA code. In order to set your own sort order, you can define it using the .Folder and it's properties. Look at the documentation for the MSDN - Folder Object and then the Items.Sort Method.
Alternatively, you can read in all the file names and sort them in your VBA-based array as discussed over in CodingHorror.
My solution is for the case when is need to merge excel files into one file in these files creation order.
Sub Main()
Dim sourceWorkbook As Workbook
Dim FSO As Object
Dim sourceFolder As Object
Dim file As Object
Dim templatePath As String, templateName As String, sourceFolderPath As String
Dim destinationFileNamePrefix As String, destinationFolderPath As String
Dim moveMergedFilesToBackup As Boolean, backupUpperFolderPath As String
Dim lastTemplateColumn As Integer, fullyFilledColumnNumber As Integer, lastSourceFileColumn As Integer, sourceFileName As String
Dim lastRow As Long, i As Long, insertExecutionNumber As Boolean, executionNumber As Long
Dim sortingWorkbook As Workbook, rowNo As Long, lastArrayIndex As Long, sourceFilesPathArray() As String
Application.ScreenUpdating = False
Call LoadSettings.LoadDataFromControlSheet(templatePath, sourceFolderPath, fullyFilledColumnNumber, destinationFolderPath, _
destinationFileNamePrefix, moveMergedFilesToBackup, backupUpperFolderPath, insertExecutionNumber)
Workbooks.Open fileName:=templatePath
templateName = Right(templatePath, Len(templatePath) - InStrRev(templatePath, "\"))
Workbooks(templateName).Activate
Call SaveFiles.SaveTemplateToTemporaryFolder(templateName)
lastTemplateColumn = Range("A1").End(xlToRight).Column
Set FSO = CreateObject("Scripting.FileSystemObject")
Set sourceFolder = FSO.Getfolder(sourceFolderPath)
'Create a new workbook for files sorting in ascending order according their creation date
Set sortingWorkbook = Workbooks.Add
'sortingWorkbook.Name = "SortingWorkbook.xlsx"
'Call SaveFiles.SaveTemplateToTemporaryFolder(sortingWorkbook.Name)
sortingWorkbook.Activate
Range("A1") = "File path"
Range("B1") = "Creation Date and Time"
'Write required data into sorting workbook
rowNo = 2
For Each file In sourceFolder.Files
sourceFileName = file.Name
If InStr(sourceFileName, ".xlsx") Then ' Only xlsx files will be merged
Range("A" & rowNo) = file.Path
Range("B" & rowNo) = file.DateCreated
rowNo = rowNo + 1
End If ' If InStr(sourceFileName, ".xlsx") Then' Only xlsx files will be merged
Next
'Sort by file creation date and time - column B
Range("A1:B1").Select
Selection.AutoFilter
ActiveWorkbook.Worksheets("Sheet1").AutoFilter.Sort.SortFields.Clear
ActiveWorkbook.Worksheets("Sheet1").AutoFilter.Sort.SortFields.Add Key:=Range _
("B1"), SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:= _
xlSortNormal
With ActiveWorkbook.Worksheets("Sheet1").AutoFilter.Sort
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
'Read filepath into array
lastArrayIndex = rowNo - 3 ' rowNo at this moment is +1 than rows, data is from 2 row, array is 0 Based, so -3
ReDim sourceFilesPathArray(lastArrayIndex) 'size array
rowNo = 2
For i = 0 To lastArrayIndex
sourceFilesPathArray(i) = Range("A" & rowNo)
rowNo = rowNo + 1
Next i
sortingWorkbook.Close saveChanges:=False
'Open source files and merge them into accumulation template
For i = 0 To lastArrayIndex
Set sourceWorkbook = Workbooks.Open(sourceFilesPathArray(i))
'Check if source file headers columns number corresponds template to which will be merged data columns number
lastSourceFileColumn = Range("A1").End(xlToRight).Column
If lastSourceFileColumn = lastTemplateColumn Then
lastRow = Cells(Rows.Count, fullyFilledColumnNumber).End(xlUp).Row
Range(Cells(2, 1), Cells(lastRow, lastSourceFileColumn)).Copy
Workbooks(templateName).Activate
lastRow = Cells(Rows.Count, fullyFilledColumnNumber).End(xlUp).Row
Range("A" & lastRow + 1).PasteSpecial
Application.CutCopyMode = False
sourceWorkbook.Close
Else
MsgBox "In the source directory was found xlsx format file" & vbNewLine & _
sourceFilesPathArray(i) & vbNewLine & _
"which has data columns number " & lastSourceFileColumn & vbNewLine & _
"which is different from template into which data are accumulated " & vbNewLine & _
"data columns number " & lastTemplateColumn & "." & vbNewLine & _
"This program will end now." & vbNewLine & _
"Check if you selected correct template and source folder or" & vbNewLine & _
"remove incorrect source file from source folder and then" & vbNewLine & _
"restart the program", vbCritical, ThisWorkbook.Name
Workbooks(templateName).Close saveChanges:=False
sourceWorkbook.Close
End
End If
Next i
Set sourceWorkbook = Nothing
Set filesObj = Nothing
Set FSO = Nothing
'Save accumulated in template data into destination folder with name formed by settings
Call SaveFiles.SaveMergedDataIntoDestination(templateName, destinationFileNamePrefix, destinationFolderPath)
Application.ScreenUpdating = True
End Sub