Rearranging Columns in Multiple Excel Files - vba

I have more than 100 excel files in .xlsx extension, Columns in all the files are not in order, i would like to re-arrange the Column order as per my Template and i would like to append the data from all files into one Output file.
i have tried the solution in this link Rearranging Columns in Multiple Excel Files using VBA and it did not work.
below are the sample files Headings for reference.
File1
Heading1,Heading2,Heading3
File2
Heading2,Heading1,Heading5,Heading7
Template File
Heading1,Heading2,Heading3,Heading4,Heading5,Heading6,Heading7
Expected Output File
FileName,Heading1,Heading2,Heading3,Heading4,Heading5,Heading6,Heading7

Try the below.
Sub Order_Columns()
Dim template_headers As Variant, header As Variant, current_header As Variant, cl As Range, col As Integer
template_headers = Array("Heading1", "Heading2", "Heading3", "Heading4", "Heading5")
For header = LBound(template_headers) To UBound(template_headers)
current_header = template_headers(header)
col = col + 1
Set cl = ActiveSheet.Rows(1).Find(What:=current_header, LookIn:=xlValues, LookAt:=xlWhole, SearchOrder:=xlByRows, SearchDirection:=xlNext, MatchCase:=False, SearchFormat:=False)
If Not cl Is Nothing Then
If Not cl.Column = col Then
Columns(cl.Column).Cut
Columns(col).Insert Shift:=xlToRight
End If
End If
Next header
End Sub
Specify your desired header order in the array
Note that headers are case-sensitive so maybe use LCase()?
I will leave with you to add code to loop over your 100+ folders to do this and then place that data in your master sheet!

Assuming that in every file you're working on sheet(1)
this would do the job:
Option Explicit
Sub ColumnMover()
Dim i As Integer, j As Integer, k As Integer, m As Integer, n As Integer
Dim mDirs As String
Dim path As String
Dim OutFile As Variant, SrcFile As Variant
Dim MyObj As Object, MySource As Object, file As Variant
OutFile = ActiveWorkbook.Name
mDirs = "c:\" 'your path here with \ in the end
file = Dir(mDirs)
While (file <> "")
path = mDirs + file
Workbooks.Open (path)
SrcFile = ActiveWorkbook.Name
n = 2
While Workbooks(OutFile).Sheets(1).Cells(n, 1).Value <> ""
n = n + 1
Wend
i = 2
While (Workbooks(OutFile).Sheets(1).Cells(1, i).Value <> "")
k = n
j = 1
While Workbooks(SrcFile).Sheets(1).Cells(1, j).Value <> Workbooks(OutFile).Sheets(1).Cells(1, i).Value And _
Workbooks(SrcFile).Sheets(1).Cells(1, j).Value <> ""
j = j + 1
Wend
If Workbooks(SrcFile).Sheets(1).Cells(1, j).Value = Workbooks(OutFile).Sheets(1).Cells(1, i).Value Then
m = 2
While Workbooks(SrcFile).Sheets(1).Cells(m, j).Value <> ""
Workbooks(OutFile).Sheets(1).Cells(k, 1).Value = path
Workbooks(OutFile).Sheets(1).Cells(k, i).Value = Workbooks(SrcFile).Sheets(1).Cells(m, j).Value
k = k + 1
m = m + 1
Wend
End If
i = i + 1
Wend
Workbooks(file).Close (False)
file = Dir
Wend
End Sub
EDIT:
Some explanation:
here the template file and the output file are the same. So first you have to have an xlsm with the structure on sheet(1):
FileName,Heading1,Heading2,Heading3,Heading4,Heading5,Heading6,Heading7
then enter the given code into this file, and run it when the output file is the active sheet.

Related

Split data into multiple workbooks based on cell value in Excel using vba

Each month I get our sales report and it contains quantities of goods we sold along with product details, and I created a template using vba where user can specify a product and it can create a excel report for them.
However, I would like to expand/modify so if I have multiple excel reports instead of just one report. I would like excel to separate however many product codes I input or listed.
Now, I added a tab called list in my template which I can list the # of product codes (the 4 digit number, in column A) where vba should read from but I need help on modifying the codes so instead of asking the user, it reads the list instead. Secondly, since master file contains all of the products and I maybe just need 20 or 30 of them, I will need the vba codes to be flexible as possible.
The way i set it up, I am basically updating/copying new info from Master file into Monthly Template and re-saving Monthly Template as product codes product as of 9.1.2017 file.
Sub monthly()
Dim x1 As Workbook, y1 As Workbook
Dim ws1, ws2 As Worksheet
Dim LR3, LR5 As Long
Dim ws3 As Worksheet
Dim Rng3, Rng4 As Range
Dim x3 As Long
Set x1 = Workbooks("Master.xlsx")
Set y1 = Workbooks("Monthly Template.xlsm")
Set ws1 = x1.Sheets("Products")
Set ws2 = y1.Sheets("Products")
Set ws3 = y1.Sheets("List")
ws2.Range("A3:AA30000").ClearContents
ws1.Cells.Copy ws2.Cells
x1.Close True
LR5 = ws3.Cells(Rows.Count, "A").End(xlUp).Row
With y1.Sheets("List")
Range("A1:A32").Sort key1:=Range("A1"), Order1:=xlAscending
End With
LR3 = ws2.Cells(Rows.Count, "A").End(xlUp).Row
Set Rng3 = ws2.Range("AC3:AC" & LR3)
Set Rng4 = ws3.Range("A1:A" & LR5)
For n = 3 To LR3
ws2.Cells(n, 29).FormulaR1C1 = "=LEFT(RC[-21], 4)"
Next n
With y1.Sheets("List")
j = .Cells(.Rows.Count, 1).End(xlUp).Row
End With
With ws2
l = .Cells(.Rows.Count, 1).End(xlUp).Row
End With
For i = 1 To j
For k = 3 To l
If Sheets("List").Cells(i, 1).Value = Sheets("Products").Cells(k, 29).Value Then
With Sheets("Output")
m = .Cells(.Rows.Count, 1).End(xlUp).Row
End With
Sheets("Output").Rows(m + 1).Value = Sheets("Products").Rows(k).Value
End If
Next k
Next i
Sheets("Output").Columns("AC").ClearContents
Dim cell As Range
Dim dict As Object, vKey As Variant
Dim Key As String
Dim SheetsInNewWorkbook As Long
Dim DateOf As Date
DateOf = DateSerial(Year(Date), Month(Date), 1)
With Application
.ScreenUpdating = False
SheetsInNewWorkbook = .SheetsInNewWorkbook
.SheetsInNewWorkbook = 1
End With
Set dict = CreateObject("Scripting.Dictionary")
With ThisWorkbook.Worksheets("List")
For Each cell In .Range("A1", .Range("A" & .Rows.Count).End(xlUp))
Key = Left(cell.Value, 4)
'Store an ArrayList in the Scripting.Dictionary that can be retrieved using the Product Key
If Not dict.exists(Key) Then dict.Add Key, CreateObject("System.Collections.ArrayList")
Next
End With
With Workbooks("Monthly Template.xlsm").Worksheets("Output")
For Each cell In .Range("H2", .Range("A" & .Rows.Count).End(xlUp))
Key = Left(cell.Value, 4)
'Add the Products to the ArrayList in the Scripting.Dictionary that is associated with the Product Key
If dict.exists(Key) Then dict(Key).Add cell.Value
Next
End With
For Each vKey In dict
If dict(vKey).Count > 0 Then
With Workbooks.Add
With .Worksheets(1)
.Name = "Products"
' .Range("A1").Value = "Products"
Workbooks("Monthly Template.xlsm").Worksheets("Output").Cells.Copy Worksheets(1).Cells
For Z = 1 To LR5
For x3 = Rng3.Rows.Count To 1 Step -1
If InStr(1, Rng3.Cells(x3, 1).Text, Workbooks("Monthly Template.xlsm").Worksheets("List").Cells(Z, 1).Text) = 0 Then
Rng3.Cells(x3, 1).EntireRow.Delete
End If
Next x3
Next Z
'.Range("A2").Resize(dict(vKey).Count).Value = Application.Transpose(dict(vKey).ToArray)
End With
.SaveAs Filename:=getMonthlyFileName(DateOf, CStr(vKey)), FileFormat:=xlOpenXMLWorkbook, CreateBackup:=False
.Close SaveChanges:=False
End With
End If
Next
With Application
.ScreenUpdating = True
.SheetsInNewWorkbook = SheetsInNewWorkbook
End With
End Sub
Function getMonthlyFileName(DateOf As Date, Product As String) As String
Dim path As String
path = ThisWorkbook.path & "\Product Reports\"
If Len(Dir(path, vbDirectory)) = 0 Then MkDir path
path = path & Format(DateOf, "yyyy") & "\"
If Len(Dir(path, vbDirectory)) = 0 Then MkDir path
path = path & Format(DateOf, "mmm") & "\"
If Len(Dir(path, vbDirectory)) = 0 Then MkDir path
getMonthlyFileName = path & "Product - " & Product & Format(DateOf, " mmm.dd.yyyy") & ".xlsx"
End Function
I seen no reason why to save copies of Monthly Template.xlsm. The OP's code simply creates a list on a worksheet and saves it to file. I might be some formatting missing that would normally get saved over from the Master File.
getMonthlyFileName(DateOf, Product) - creates a file path (Root Path\Year of Date\Month of Date\Product - Prodcut mmm.dd.yyyy.xlsx. In this way, the Product files can be stored in an easy to lookup structure.
Sub CreateMonthlyReports()
Dim cell As Range
Dim dict As Object, vKey As Variant
Dim Key As String
Dim SheetsInNewWorkbook As Long
Dim DateOf As Date
DateOf = DateSerial(Year(Date), Month(Date), 1)
With Application
.ScreenUpdating = False
SheetsInNewWorkbook = .SheetsInNewWorkbook
.SheetsInNewWorkbook = 1
End With
Set dict = CreateObject("Scripting.Dictionary")
With ThisWorkbook.Worksheets("List")
For Each cell In .Range("A1", .Range("A" & .Rows.Count).End(xlUp))
Key = Left(cell.Value, 4)
'Store an ArrayList in the Scripting.Dictionary that can be retrieved using the Product Key
If Not dict.exists(Key) Then dict.Add Key, CreateObject("System.Collections.ArrayList")
Next
End With
With Workbooks("Master.xlsx").Worksheets("Products")
For Each cell In .Range("H2", .Range("H" & .Rows.Count).End(xlUp))
Key = Left(cell.Value, 4)
'Add the Products to the ArrayList in the Scripting.Dictionary that is associated with the Product Key
If dict.exists(Key) Then dict(Key).Add cell.Value
Next
End With
For Each vKey In dict
If dict(vKey).Count > 0 Then
With Workbooks.Add
With .Worksheets(1)
.Name = "Products"
.Range("A1").Value = "Products"
.Range("A2").Resize(dict(vKey).Count).Value = Application.Transpose(dict(vKey).ToArray)
End With
.SaveAs FileName:=getMonthlyFileName(DateOf, CStr(vKey)), FileFormat:=xlOpenXMLWorkbook, CreateBackup:=False
.Close SaveChanges:=False
End With
End If
Next
With Application
.ScreenUpdating = True
.SheetsInNewWorkbook = SheetsInNewWorkbook
End With
End Sub
Function getMonthlyFileName(DateOf As Date, Product As String) As String
Dim path As String
path = ThisWorkbook.path & "\Product Reports\"
If Len(Dir(path, vbDirectory)) = 0 Then MkDir path
path = path & Format(DateOf, "yyyy") & "\"
If Len(Dir(path, vbDirectory)) = 0 Then MkDir path
path = path & Format(DateOf, "mmm") & "\"
If Len(Dir(path, vbDirectory)) = 0 Then MkDir path
getMonthlyFileName = path & "Product - " & Product & Format(DateOf, " mmm.dd.yyyy") & ".xlsx"
End Function
Try two loops for this, making sure you sort by the product in the main list to make this a little quicker.
Dim i as Long, j as Long, k as Long, l as Long, m as Long
With Sheets("List")
j = .Cells( .Rows.Count, 1).End(xlUp).Row
End With
With Sheets("Products")
l = .Cells( .Rows.Count, 1).End(xlUp).Row
End With
For i = 2 to j
For k = 2 to l
If Sheets("List").Cells(i,1).Value = Sheets("Products").Cells(k,1).Value Then
With Sheets("Output")
m = .Cells( .Rows.Count, 1).End(xlUp).Row
End With
Sheets("Output").Rows(m+1).Value = Sheets("Products").Rows(k).Value
End If
Next k
Next i
Edit
Will try to piecemeal something to give at least a lead to splitting into different sheets, rather than having one output sheet (this will not be tested, just free-coding):
Dim i as Long, j as Long, k as Long, l as Long, m as Long, n as String
With Sheets("List")
j = .Cells( .Rows.Count, 1).End(xlUp).Row
End With
With Sheets("Products")
l = .Cells( .Rows.Count, 1).End(xlUp).Row
End With
For i = 2 to j
n = Sheets("List").Cells(i,1).Value
Sheets.Add(After:=Sheets(Sheets.Count)).Name = n
Sheets(n).Cells(1,1).Value = n
Sheets(n).Rows(2).Value = Sheets("Products").Rows(1).Value
For k = 2 to l
With Sheets(n)
If .Cells(1,1).Value = Sheets("Products").Cells(k,1).Value Then
m = .Cells( .Rows.Count, 1).End(xlUp).Row
.Rows(m+1).Value = Sheets("Products").Rows(k).Value
End If
Next k
Next i
I don't know why some people doing VBA thinks declaring all the variables with weird names before a thousand lines of code is a good idea.........
Anyways..back to the question, I believe what you are trying to achieve is:
1) Specify a list whilst the code iterates through the list and filters the data based on the listed items.
2) Creates a workbook where the filtered the data is copied over.
3) saving the workbook to somewhere you'll specify, with a specific name.
So naturally, your programme access point should be the one that iterates through the specified list, which should be your main function.
Then inside main function you'll have a Sub that deals with whatever the product ID is, and then filters on your product ID, then copies the data into a newly created workbook.
Last step would be naming the new workbook and saving it close it.
So here is some code skeleton that hopefully will help you with creating the monthly reports. You'll have to write yourself how you want to copy the data from your master workbook to the destination workbook (it should be simple enough, just filter the source list and copy the results to the destination workbook, no dictionary nor arraylist is needed).
Sub main()
Dim rngIdx As Range
Set rngIdx = ThisWorkbook.Sheets("where your list is").Range("A1")
With Application
.DisplayAlerts = False
.ScreenUpdating = False
End With
While (rngIdx.Value <> "")
Call create_report(rngIdx.Value)
Set rngIdx = rngIdx.Offset(1, 0)
Wend
With Application
.DisplayAlerts = True
.ScreenUpdating = True
End With
End Sub
Sub create_report(ByVal product_ID As String)
Dim dest_wbk As Workbook
Set dest_wbk = Workbooks.Add
Call do_whatever(ThisWorkbook, dest_wbk, product_ID)
dest_wbk.SaveAs getMonthlyFileName(some_date, product_ID)
dest_wbk.Close
End Sub
Sub do_whatever(source_wbk As Workbook, dest_wbk As Workbook, ByVal product_ID As String)
' this is the code where you copy from your master data to the destination workbook
' modify sheet names, formatting.......etc.
End Sub

Excel VBA .Saveas() function to preseve formating

I'm trying to use the below saveas() function to output an excel worksheet to a xltext file. the file is generated fine but numbers with a special formatting of #,###.00. are output as "1,000.00" rather than just 1,000.00. How can I remove these double quotes.
Dim tab_output_line, tab_output_head, tab_source As String
Dim File_Location As String
tab_output_line = "Upload_PO_LineItem"
tab_source = "PBOOK"
File_Location = Sheets(tab_source).Range("S5").Value
Sheets(tab_output_line).Select
ChDir File_Location
ActiveWorkbook.SaveAs Filename:= _
File_Location + tab_output_line + ".txt", FileFormat:=xlText, _
CreateBackup:=False
Your current code is making a tab-delimited file. Cells that contain commas (even as formatted) are being "protected" by being enclosed in double quotes.
To avoid this "protection", try code like this:
Sub tony2()
Dim N As Long, i As Long, j As Long, Record As String
Dim M As Long
N = Cells(Rows.Count, 1).End(xlUp).Row
Close #2
Open "C:\Users\Garys\desktop\tony.txt" For Output As #2
For i = 1 To N
Record = ""
M = Cells(i, Columns.Count).End(xlToLeft).Column
For j = 1 To M
Record = Record & vbTab & Cells(i, j).Text
Next j
Record = Mid(Record, 2)
Print #2, Record
Next i
Close #2
End Sub
Input:
and output:

Loop Through files in a folder and paste filename onto spreadsheet

I am totally new to VBA, looking for tips or hints to solve this question.
I am trying to loop through all the files in a folder and trying to split the filename into three parts that are separated by underscore and then paste those into a spreadsheet. After that, pivot it and count how many files there are in a new sheet.
For example, Filename : CA_File_20170810.txt
So it looks like this:
**IPA TYPE DATE Filename Filepath**
CA File 20170810
*IPA, Type, Date,filename, filepath are columns headers in excel.
Here is what I have in my code so far
Sub LoopingThroughFiles()
Dim f As String
Dim G As String
Dim File As Variant
Dim MyObj As Object
Dim MySource As Object
Dim FileName As Variant
Dim TypeName As Variant
Cells(1, 1) = "IPA"
Cells(1, 2) = "TYPE"
Cells(1, 3) = "DATE"
Cells(1, 4) = "FILENAME"
Cells(1, 5) = "FILEPATH"
Cells(2, 1).Select
f = Dir("C:\Users\kxc8574\Documents\VBA_Practice\")
G = Dir("C:\Users\kxc8574\Documents\VBA_Practice\")
If Right(f, 1) <> "\" Then
f = f + "\"
Cells(2, 1).Select
Do While Len(f) > 0
IpaName = Left(f, InStr(f, "_") - 1)
ActiveCell.Formula = IpaName
ActiveCell.Offset(1, 0).Select
f = Dir()
Loop
Do While Len(G) > 0
TypeName = Mid(G, InStr(G, "_") + 1, InStr(G, "File_") - InStr(G, "_") - 1)
ActiveCell.Formula = TypeName
ActiveCell.Offset(1, 0).Select
G = Dir()
Loop
End If
End Sub
I am missing a lot of things, not sure how to really continue. This code gives me an error "invalid procedure call" when it reaches the G = Dir()
Thanks for your help !!!
First, paste the text under "Explanation" into A1 of a worksheet. Then paste the code under "Code" into a module. Make sure the workbook is in the same directory as your .txt files. Then, run the macro. See animated gif for the result.
"Explanation"
This workbook contains a macro which will
1) Make a new sheet in this workbook named "Combined"
2) Open a copy of each .txt file located in the same directory as this workbook
3) extract the text between "_" characters
4) place the separated text into columns
5) count the number of .txt files processed
Note: Any sheet named "Combined" in this Workbook will be deleted
"Code"
Option Explicit
Sub CombineFiles()
Dim theDir As String, theFile As String
Dim sh As Worksheet, wk As Workbook, newSheet As Worksheet
Dim r As Range, parts() As String
Dim i As Long, s As String
Dim Done As Boolean, numFiles As Integer
Const ext = ".txt"
Err.Clear
theDir = ThisWorkbook.Path
'explain what program does
Worksheets("Program").Select
For i = 1 To 7
s = s & Cells(i, 1) & vbCr & vbCr
Next i
s = s & vbCr
s = MsgBox(s, vbYesNoCancel, "What this macro does")
If s <> vbYes Then End
For Each sh In Worksheets
If sh.Name = "Combined" Then
Application.DisplayAlerts = False
sh.Delete
Application.DisplayAlerts = True
End If
Next
Set newSheet = ThisWorkbook.Sheets.Add
newSheet.Name = "Combined"
'Loop through all files in directory with ext
s = Dir(theDir & "\*" & ext)
Set r = Range("A1")
r = "IPA"
r.Offset(0, 1) = "Type"
r.Offset(0, 2) = "Date"
r.Offset(0, 3) = "filename"
r.Offset(0, 4) = "filepath"
While s <> ""
numFiles = numFiles + 1
parts = Split(s, "_")
Set r = r.Offset(1, 0)
For i = 0 To 2
r.Offset(, i) = Replace(parts(i), ".txt", "")
Next i
r.Offset(, 3) = s
r.Offset(, 4) = theDir & "\" & s & ext
s = Dir()
Wend
MsgBox (numFiles & " files were processed.")
End Sub
Untested but should give you some idea:
Sub LoopingThroughFiles()
Const FPATH As String = "C:\Users\kxc8574\Documents\VBA_Practice\"
Dim f As String, i As Long, arr, sht As Worksheet
Set sht = ActiveSheet
sht.Cells(1, 1).Resize(1, 5).Value = _
Array("IPA", "TYPE", "DATE", "FILENAME", "FILEPATH")
f = Dir(FPATH & "*.txt") '<< only txt files
i = 2
Do While f <> ""
'split filename on underscore after replacing the ".txt"
arr = Split(Replace(f, ".txt", ""), "_", 3)
sht.Cells(i, 1).Resize(1, UBound(arr) + 1).Value = arr
sht.Cells(i, 4).Value = f
sht.Cells(i, 5).Value = FPATH
f = Dir() '<< next file
i = i + 1
Loop
End Sub
Untested but perhaps something like this??
Sub HashFiles()
Dim MyDir As String, MyIPA As Variant, MyType As Variant, MyDate As Variant, i As Integer, oFile As Object, oFSO As Object, oFolder As Object, oFiles As Object
MyDir = "C:\Users\kxc8574\Documents\VBA_Practice\"
Set oFSO = CreateObject("Scripting.FileSystemObject")
Set oFolder = oFSO.GetFolder(MyDir)
Set oFiles = oFolder.Files
ReDim MyIPA(1 To oFiles.Count)
ReDim MyType(1 To oFiles.Count)
ReDim MyDate(1 To oFiles.Count)
i = 1
For Each oFile In oFiles
MyIPA(i) = Split(oFile.Name, "_")(0)
MyType(i) = Split(oFile.Name, "_")(1)
MyDate(i) = Split(oFile.Name, "_")(2)
i = i + 1
Next
Range("A2").Resize(UBound(MyIPA) + 1, 1) = Application.Transpose(MyIPA)
Range("B2").Resize(UBound(MyType) + 1, 1) = Application.Transpose(MyType)
Range("C2").Resize(UBound(MyDate) + 1, 1) = Application.Transpose(MyDate)
End Sub

Create multiple text files for selected data on excel using vba scripting in single click

I am using the code below to generate the single file for selected range and considering the first cell in the selected range as file name. Please find the image below for more details[This image shows the selected range,Consider K column(Firstline) and N Column( Lastline) to be in one file and other set of 1st and last line in other file ]this image shows the print file for a single file this is the way m currently using for generating files.I need to create more 30k files so please help me to create more files in single click considering the first and last line as header and footer for the file
Private Sub CommandButton1_Click()
Dim myFile As String, rng As Range, cellValue As Variant, i As Integer, j As Integer, path As String, filename, filename2 As String
path = "D:\Watchlist-Files\"
filename = Selection.Cells(1, 1).Value
filename2 = Left(Mid(filename, 32, 99), Len(Mid(filename, 32, 99)) - 2)
myFile = path & filename2
Set rng = Selection
Open myFile For Output As #1
For i = 1 To rng.Rows.Count
For j = 1 To rng.Columns.Count
cellValue = rng.Cells(i, j).Value
If j = rng.Columns.Count Then
Print #1, cellValue
Else
Print #1, cellValue,
End If
Next j
Next i
Close #1
End Sub
The code below is using a Loop that scans rows in a range that consists of Columns K:N (according to your attached screen-shots).
Assumptions made: your FirstLine is in Column K, and it's the marker of the start position of copying the first cell in the first row.
Your LastLine is in Column N, and it's the marker of the last cell to copy, this is why I am closing the file once it is found.
Edit 1: added a Msgbox to allow the user selection of exporting the entire range or not. In case the user selected NO, then a second InputBox appears that allows the user to enter manually the last row number to export.
Option Explicit
Public Sub CommandButton1_Click()
Dim myFile As String
Dim rng As Range
Dim cellValue As Variant
Dim i As Long
Dim j As Long
Dim LastRow As Long
Dim path As String
Dim filename As String
Dim response As Boolean
path = "D:\Watchlist-Files\"
response = MsgBox("Do you want to Export the entire Range ? ", vbYesNo)
' Export the entire Range
If response = vbYes Then
LastRow = Cells(Rows.Count, "N").End(xlUp).Row
Else ' enter in the inputbox the last row number you want to export
LastRow = InputBox("Enter Last Row Number you wsnt to Export")
End If
Set rng = Range("K2:N" & LastRow)
For i = 2 To LastRow
' Column K holds the file name
If Cells(i, 11) <> "" Then
filename = Left(Mid(Cells(i, 11).Value, 32, 99), Len(Mid(Cells(i, 11).Value, 32, 99)) - 2)
myFile = path & filename
Open myFile For Output As #1
End If
For j = 1 To rng.Columns.Count
cellValue = Cells(i, 10 + j).Value
If j = rng.Columns.Count Then
Print #1, cellValue
' found LastLine >> close the file
If Not cellValue = "" Then
Close #1
End If
Else
Print #1, cellValue,
End If
Next j
Next i
End Sub
Edit 2: Added new code below (to keep the first option valid). The user needs to confirm that every selection he makes start and ends with FirstLine and LastLine , there is no error handling.
Option Explicit Section
Option Explicit
Dim filename As String
Dim path As String
Dim myFile As String
Dim rng As Range
Dim j As Long
Public Sub CommandButton1_Click
Public Sub CommandButton1_Click()
Dim lastRow As Long
Dim Sel_Range As Long
Dim response As Boolean
Dim rowStart() As Long
Dim rowFinish() As Long
path = "D:\Watchlist-Files\"
response = MsgBox("Do you want to Export only the Selected Range ? ", vbYesNo)
If response = True Then
Set rng = Selection
ReDim rowStart(1 To Selection.Areas.Count)
ReDim rowFinish(1 To Selection.Areas.Count)
For Sel_Range = 1 To Selection.Areas.Count
rowStart(Sel_Range) = Selection.Areas(Sel_Range).Row
rowFinish(Sel_Range) = Selection.Areas(Sel_Range).Row + Selection.Areas(Sel_Range).Rows.Count - 1
Call CreateTextFiles(rowStart(Sel_Range), rowFinish(Sel_Range))
Next Sel_Range
Else ' export the entire Range in Columns K:N
lastRow = Cells(Rows.Count, "N").End(xlUp).Row
Set rng = Range("K2:N" & lastRow)
Call CreateTextFiles(2, lastRow)
End If
Sub CreateTextFiles(Sel_StartRow As Long, Sel_FinishRow As Long) - new routine to allow handling of multiple Ranges selection
Sub CreateTextFiles(Sel_StartRow As Long, Sel_FinishRow As Long)
Dim i As Long
Dim cellValue As Variant
For i = Sel_StartRow To Sel_FinishRow
' Column K holds the file name
If Cells(i, 11) <> "" Then
filename = Left(Mid(Cells(i, 11).Value, 32, 99), Len(Mid(Cells(i, 11).Value, 32, 99)) - 2)
myFile = path & filename
Open myFile For Output As #1
End If
For j = 1 To rng.Columns.Count
cellValue = Cells(i, 10 + j).Value
If j = rng.Columns.Count Then
Print #1, cellValue
' found LastLine >> close the file
If Not cellValue = "" Then
Close #1
End If
Else
Print #1, cellValue,
End If
Next j
Next i
End Sub

How can I open this VBA workbook faster?

I am currently trying to make a macro that will go to a directory, open a workbook (there are 38 currently with an eventual total of 52), filter two columns, get the total (repeat this 4 times), and the close the workbook. Currently it takes my application about 7 minutes just to process the current 38 workbooks.
How can I speed this up? I have already disables screen updating, events, and I changed the calculation methods to xlCalculationManual. I don't know if it common practice but I have seen people asking about a way to access a workbook without it being open but the suggestion to turn off screen updating is always made, which I have done.
When I run it in debug mode the Workbooks.Open() can take up to 10 seconds. The file directory is actually on a company network but accessing the file normally barely takes any time, under 5 seconds.
The data in the workbooks can contain the same points but at a different status. I do not think combining all of the data into one workbook would be possible.
I am going to experiment with direct cell references. Once I have some results I will update my post.
Private UNAME As String
Sub FileOpenTest()
Call UserName
Dim folderPath As String
Dim filename As String
Dim tempFile As String
Dim wb As Workbook
Dim num As Integer
Dim values(207) As Variant
Dim arryindex
Dim numStr As String
Dim v As Variant
Dim init As Integer
init = 0
num = 1
arryindex = 0
numStr = "0" & CStr(num)
'Initialize values(x) to -1
For Each v In values
values(init) = -1
init = init + 1
Next
With Excel.Application
.ScreenUpdating = False
.Calculation = Excel.xlCalculationManual
.EnableEvents = False
.DisplayAlerts = False
End With
'File path to save temp file
tempFile = "C:\Users\" & UNAME & "\Documents\TEMP.xlsm"
'Directory of weekly reports
folderPath = "path here"
'First file to open
filename = Dir(folderPath & "file here" & numStr & ".xlsm")
Do While filename <> ""
Set wb = Workbooks.Open(folderPath & filename)
'Overwrite previous "TEMP.xlsm" workbook without alert
Application.DisplayAlerts = False
'Save a temporary file with unshared attribute
wb.SaveAs filename:=tempFile, AccessMode:=xlExclusive
'operate on file
Filters values, arryindex
wb.Close False
'Reset file name
filename = Dir
'I use this loop to add the appropriate number to the end ie 01, 02, 03 etc
If num >= 9 Then
num = num + 1
If num = 33 Then
num = num + 1
End If
numStr = CStr(num)
ElseIf num < 9 Then
num = num + 1
numStr = "0" & CStr(num)
End If
filename = Dir(folderPath & "filename here" & numStr & ".xlsm")
Loop
output values
'Delete "TEMP.xlsm" file
On Error Resume Next
Kill tempFile
On Error GoTo 0
End Sub
Function Filters(ByRef values() As Variant, ByRef arryindex)
On Error Resume Next
ActiveSheet.ShowAllData
On Error GoTo 0
'filter column1
ActiveSheet.Range("B6").End(xlDown).AutoFilter Field:=2, Criteria1:=Array( _
"p1", "p2", "p3", "p4", "p5"), Operator:=xlFilterValues
'filter column2
ActiveSheet.Range("J6").End(xlDown).AutoFilter Field:=10, Criteria1:=Array( _
"s1", "d2", "s3"), Operator:=xlFilterValues
'get the total of points
values(arryindex) = TotalCount
arryindex = arryindex + 1
'filter column2 for different criteria
ActiveSheet.Range("J6").End(xlDown).AutoFilter Field:=10, Criteria1:="s"
'filter colum3 for associated form
ActiveSheet.Range("AZ6").End(xlDown).AutoFilter Field:=52, Criteria1:="<>"
'get the total of points
values(arryindex) = TotalCount
arryindex = arryindex + 1
'filter coum 3 for blank forms
ActiveSheet.Range("AZ6").End(xlDown).AutoFilter Field:=52, Criteria1:="="
'get the total of points
values(arryindex) = TotalCount
arryindex = arryindex + 1
'filter for column4 if deadline was made
ActiveSheet.Range("J6").End(xlDown).AutoFilter Field:=52
ActiveSheet.Range("J6").End(xlDown).AutoFilter Field:=10, Criteria1:=Array( _
"s1", "s2", "s3", "s4", "s5", "s6"), Operator:=xlFilterValues
ActiveSheet.Range("BC6").End(xlDown).AutoFilter Field:=55, Criteria1:=RGB(146 _
, 208, 80), Operator:=xlFilterCellColor
'get total of points
values(arryindex) = TotalCount
arryindex = arryindex + 1
End Function
Public Function TotalCount() As Integer
Dim rTable As Range, r As Range, Kount As Long
Set rTable = ActiveSheet.AutoFilter.Range
TotalCount = -1
For Each r In Intersect(Range("A:A"), rTable)
If r.EntireRow.Hidden = False Then
TotalCount = TotalCount + 1
End If
Next
End Function
Function UserName() As String
UNAME = Environ("USERNAME")
End Function
Function output(ByRef values() As Variant)
Dim index1 As Integer
Dim index2 As Integer
Dim t As Range
Dim cw As Integer
'Calendar week declariations
Dim cwstart As Integer
Dim cstart As Integer
Dim cstop As Integer
Dim data As Integer
data = 0
start = 0
cw = 37
cstart = 0
cstop = 3
ThisWorkbook.Sheets("Sheet1").Range("B6").Activate
For index1 = start To cw
For index2 = cstart To cstop
Set t = ActiveCell.Offset(rowOffset:=index1, columnOffset:=index2)
t.value = values(data)
data = data + 1
Next
Next
End Function
In general there are five rules to making Excel-VBA macros fast:
Don't use .Select methods,
Don't use Active* objects more than once,
Disable screen-updating and automatic calculations,
Don't use visual Excel methods (like Search, Autofilter, etc),
And most of all, always use range-array copying instead of browsing individual cells in a range.
Of these, you have only implemented #3. Additionally, you are exacerbating things by re-Saving your worksheets, just so that you can execute Visual modification methods (AutoFilter in your case). What you need to do to make it fast is to first implement the rest of these rules, and secondly, stop modifying your source worksheets so that you can open them read-only.
The core of what's causing your problems and forcing all of these other undesirable decisions is how you have implemented the Filters function. Instead of trying to do everything with the visual Excel functions, which are slow compared to (well-written) VBA (and that modify the worksheets, forcing your redundant Saves), just range-array copy all of the data you need from the sheet and use straight-forward VBA code to do your counting.
Here is an example of your Filters function that I converted to these principles:
Function Filters(ByRef values() As Variant, ByRef arryindex)
On Error GoTo 0
Dim ws As Worksheet
Set ws = ActiveSheet
'find the last cell that we might care about
Dim LastCell As Range
Set LastCell = ws.Range("B6:AZ6").End(xlDown)
'capture all of the data at once with a range-array copy
Dim data() As Variant, colors() As Variant
data = ws.Range("A6", LastCell).Value
colors = ws.Range("BC6", "BC" & LastCell.Row).Interior.Color
' now scan through every row, skipping those that do not
'match the filter criteria
Dim r As Long, c As Long, v As Variant
Dim TotCnt1 As Long, TotCnt2 As Long, TotCnt3 As Long, TotCnt4 As Long
TotCnt1 = -1: TotCnt2 = -1: TotCnt3 = -1: TotCnt4 = -1
For r = 1 To UBound(data, 1)
'filter column1 (B6[2])
v = data(r, 2)
If v = "p1" Or v = "p2" Or v = "p3" Or v = "p4" Or v = "p5" Then
'filter column2 (J6[10])
v = data(r, 10)
If v = "s1" Or v = "d2" Or d = "s3" Then
'get the total of points
TotCnt1 = TotCnt1 + 1
End If
'filter column2 for different criteria
If data(r, 10) = "s" Then
'filter colum3 for associated form
If CStr(data(r, 52)) <> "" Then
'get the total of points
TotCnt2 = TotCnt2 + 1
Else
' filter coum 3 for blank forms
'get the total of points
TotCnt3 = TotCnt3 + 1
End If
End If
'filter for column4 if deadline was made
v = data(r, 10)
If v = "s1" Or v = "s2" Or v = "s3" Or v = "s4" Or v = "s5" Then
If colors(r, 1) = RGB(146, 208, 80) Then
TotCnt4 = TotCnt4 + 1
End If
End If
End If
Next r
values(arryindex) = TotCnt1
values(arryindex + 1) = TotCnt2
values(arryindex + 2) = TotCnt3
values(arryindex + 3) = TotCnt4
arryindex = arryindex + 4
End Function
Please note that because I cannot test this for you and also because there is a lot of implicitness to the Autofilter/Range effects in the original code, I cannot tell if it is correct. You will have to do that.
Note: If you do decided to implement this, please let us know what impact it had, if any. (I try to keep track of what works and how much)