Keep Array value when Sub is looping - arrays

I currently have this code that finds all files and folders and writes it to a table. The problem is is that is it sometimes slow.
The code below is modified so that it writes to an array but I am having issues passing the array on when the code loops.
Ultimately, I would like the array to pass on to the first sub so that I can transpose it into the table at once.
Sub FileAndFolder()
Dim FSOLibrary As Object
Dim FSOFolder As Object
Dim FolderName As String
Dim FilesTbl As ListObject
Set FilesTbl = Range("FilesTbl").ListObject
'Set the folder name to a variable
FolderName = Left$(ActiveWorkbook.Path, InStrRev(ActiveWorkbook.Path, "\"))
'Set the reference to the FSO Library
Set FSOLibrary = CreateObject("Scripting.FileSystemObject")
'Another Macro must call LoopAllSubFolders Macro to start
LoopAllFolders FSOLibrary.GetFolder(FolderName)
'return TempArray here and paste into table
'Range(FilesTbl.ListColumns("File Name").DataBodyRange(1)) = TempArray
End Sub
Sub LoopAllFolders(FSOFolder As Object)
'Don’t run the following macro, it will be called from the macro above
Dim FSOSubFolder As Object
Dim FSOFile As Object
Dim FolderPath As String
Dim FileName As String
Dim TempArray() As String
'For each subfolder call the macro
For Each FSOSubFolder In FSOFolder.SubFolders
LoopAllFolders FSOSubFolder
Next
'For each file, print the name
For Each FSOFile In FSOFolder.Files
'Insert the actions to be performed on each file
FileName = FSOFile.Name
FolderPath = FSOFile.ParentFolder
If Left(FileName, 2) = "~$" Then GoTo NEXTINLOOP
ReDim Preserve TempArray(0 To 3, 0 To i)
TempArray(0, i) = FileName
TempArray(1, i) = FolderPath & "\" & FileName 'file
TempArray(2, i) = FolderPath 'folder
TempArray(3, i) = FolderPath & "\" & FileName 'showpath
i = i + 1
NEXTINLOOP:
Next
End Sub 'TempArray and i clears here
Thanks.

You either need to declare a variable at the module level so that the list of folder information is available to all methods in the module, or change 'LoopAllFolders' to a Function so that you can return the information you have collated.
The function below will return a Variant which contains an array of arrays (normally called a jagged array). You access the jagged array using this nomenclature
Varname(x)(y)
You will need a variable in the calling method to receive the jagged array
e.g.
Dim myFileInfo as Variant
MyFileInfo = LoopAllFolders(FSOLibrary.GetFolder(FolderName))
Here is the updated function
Public Function LoopAllFolders(FSOFolder As Scripting.FileSystemObject) As Variant
'Don’t run the following macro, it will be called from the macro above
Dim FileInfo As Scripting.Dictionary: Set myFileInfo = New Scripting.Dictionary
'For each subfolder call the macro
Dim FSOSubFolder As Scripting.Folder
For Each FSOSubFolder In FSOFolder.SubFolders
LoopAllFolders FSOSubFolder
Next
'For each file, print the name
Dim FSOFile As Scripting.File
For Each FSOFile In FSOFolder.Files
'Insert the actions to be performed on each file
Dim FileName As String
FileName = FSOFile.Name
Dim FolderPath As String
FolderPath = FSOFile.ParentFolder
If Not Left(FileName, 2) = "~$" Then
myFileInfo.Add Array(FileName, FolderPath & "\" & FileName, FolderPath, FolderPath & "\" & FileName)
End If
Next
LoopAllFolders = myFileInfo.Items
End Function
The above code may not be perfect but at least it points you in the right direction.
Based on your question, you might do well by working through a VBA tutorial as functions are fairly fundamental, and if you are unaware of them......
To help you on your journey I'd also recommend installing the fantastic and free RubberDuck addin.

Create a List of Files all subfolders of a Folder
I do not understand what you're doing with FilesTbl, so I modified your solution to create a new workbook with the result. Surely you will figure out how to apply it to the table.
A Quick Fix
Option Explicit
Sub FileAndFolder()
Dim FSOLibrary As Object
Dim FSOFolder As Object
Dim FolderName As String
Dim FilesTbl As ListObject
'Set FilesTbl = Range("FilesTbl").ListObject
'Set the folder name to a variable
FolderName = Left$(ActiveWorkbook.Path, InStrRev(ActiveWorkbook.Path, "\"))
'Set the reference to the FSO Library
Set FSOLibrary = CreateObject("Scripting.FileSystemObject")
Dim TempArray() As Variant ' ByRef
'Another Macro must call LoopAllSubFolders Macro to start
LoopAllFolders FSOLibrary.GetFolder(FolderName), TempArray
'return TempArray here and paste into table
With Workbooks.Add
With ActiveSheet.Range("A1").Resize(UBound(TempArray, 2), UBound(TempArray))
.Value = Application.Transpose(TempArray)
End With
.Saved = True
End With
'Range(FilesTbl.ListColumns("File Name").DataBodyRange(1)) = TempArray
End Sub
Sub LoopAllFolders(FSOFolder As Object, ByRef TempArray As Variant)
'Don’t run the following macro, it will be called from the macro above
Dim FSOSubFolder As Object
Dim FSOFile As Object
Dim FolderPath As String
Dim FileName As String
Dim i As Long
'Dim TempArray() As String
'For each subfolder call the macro
For Each FSOSubFolder In FSOFolder.SubFolders
LoopAllFolders FSOSubFolder, TempArray
Next
'For each file, print the name
For Each FSOFile In FSOFolder.Files
'Insert the actions to be performed on each file
FileName = FSOFile.Name
FolderPath = FSOFile.ParentFolder
If Left(FileName, 2) = "~$" Then GoTo NEXTINLOOP
i = i + 1
ReDim Preserve TempArray(1 To 4, 1 To i)
TempArray(1, i) = FileName
TempArray(2, i) = FolderPath & "\" & FileName 'file
TempArray(3, i) = FolderPath 'folder
TempArray(4, i) = FolderPath & "\" & FileName 'showpath
NEXTINLOOP:
Next
End Sub 'TempArray and i clears here

Related

Importing multiple selected files, properly spliced, into multiple sheets via VBA

First time I ask a question here, but so far answers from this forum have always helped me a lot. However, now I have been dealing with a problem for weeks and unfortunately never found sufficient answers.
The current task is to update an already existing macro regarding data processing and display to a newer UI and increased usability. However, in the same sense I would like to improve the speed of the macro.
My current problem is opening, loading, splitting and pasting multiple selected .txt files into appropriately named sheets. The files can very quickly have over a hundred thousand entries of data, all separated by either space or enter. For importing and splitting a single file, I stumbled across this code, which I have subsequently adapted for my circumstances:
Private Sub testModule1()
Dim arr, tmp, output
Dim Datei
Dim FSO
Dim x, y As Integer
Dim str_string, filePath As String
Set FSO = CreateObject("Scripting.FilesystemObject")
filePath = Application.GetOpenFilename
Set Datei = FSO.OpentextFile(filePath)
str_string = Datei.readall
Datei.Close
arr = Split(str_string, vbCrLf)
ReDim output(UBound(arr), 50)
For x = 0 To UBound(arr)
tmp = Split(arr(x), " ")
For y = 0 To UBound(tmp)
output(x, y) = tmp(y)
Next
Next
Sheets("Sheet1").Range("A1").Resize(UBound(output) + 1, UBound(output, 2)) = output
End Sub
This part lets me select a single file, splits the cells as desired, and finally posts it to the first sheet.
For importing multiple files and naming the sheet after the file name, I found code here (the first solution):
https://www.mrexcel.com/board/threads/importing-multiple-text-files-in-to-multiple-work-sheets-with-text-file-names.1147363/
However, it currently opens in a new workbook, Windows tells me several times that data is overwritten when I do this, and the delimiter is also limited to only one character.
I am currently failing to find a reasonable combination of these two actions. The direct opening and reasonable splitting of several selected files and the subsequent integrating into several sheets named accordingly by file name.
In the old version of the macro, all file paths were first retrieved and stored in cells for this purpose, and later looped through these cells while reading and integrating the individual data in the process. However, everything in a sheet and rather, as I find, cumbersome.
I hope to find a more elegant solution for this problem than having to store data in sheets during editing and I am also happy about other suggestions and solutions.
EDIT:
After the hint from Solar Mike I was able to adapt the code to this:
Private Sub testModule2()
Dim fDialog As FileDialog
Dim fPath As Variant
Dim FSO
Dim Datei
Dim arr, tmp, output
Dim file, fileName As String
Dim x, y As Integer
Dim newSht As Worksheet
Application.ScreenUpdating = False
Set fDialog = Application.FileDialog(msoFileDialogFilePicker)
With fDialog
.AllowMultiSelect = True
.Title = "Please select files to import"
.Filters.Clear
.Filters.Add "VBO Files", "*.vbo"
If .Show = True Then
For Each fPath In .SelectedItems
Set FSO = CreateObject("Scripting.FilesystemObject")
fileName = FSO.GetFilename(fPath)
Set Datei = FSO.OpentextFile(fPath)
file = Datei.readall
Datei.Close
arr = Split(file, vbCrLf)
ReDim output(UBound(arr), 50)
For x = 0 To UBound(arr)
tmp = Split(arr(x), " ")
For y = 0 To UBound(tmp)
output(x, y) = tmp(y)
Next
Next
Set newSht = ActiveWorkbook.Sheets.Add(after:=ActiveWorkbook.Worksheets(ActiveWorkbook.Worksheets.Count))
newSht.Name = fileName
Sheets(fileName).Range("A1").Resize(UBound(output) + 1, UBound(output, 2)) = output
Next
End If
End With
Application.ScreenUpdating = True
End Sub
This does what is actually required, but just importing only 5 files already takes about a minute. Since on average up to 20 files can/should be imported and the processing of the data still takes place afterwards, this still seems a bit much to me.
It should be noted that the data sets are reduced again during processing, something between 40 and 80% of the data are filtered. Unfortunately I don't have the expertise to do this before, even if this would reduce the loading time.
Does this do what you want?
Sub CombineTextFiles()
'updateby Extendoffice
Dim xFilesToOpen As Variant
Dim I As Integer
Dim xWb As Workbook
Dim xTempWb As Workbook
Dim xDelimiter As String
Dim xScreen As Boolean
On Error GoTo ErrHandler
xScreen = Application.ScreenUpdating
Application.ScreenUpdating = False
xDelimiter = "|"
xFilesToOpen = Application.GetOpenFilename("Text Files (*.txt), *.txt", , "Kutools for Excel", , True)
If TypeName(xFilesToOpen) = "Boolean" Then
MsgBox "No files were selected", , "Kutools for Excel"
GoTo ExitHandler
End If
I = 1
Set xTempWb = Workbooks.Open(xFilesToOpen(I))
xTempWb.Sheets(1).Copy
Set xWb = Application.ActiveWorkbook
xTempWb.Close False
xWb.Worksheets(I).Columns("A:A").TextToColumns _
Destination:=Range("A1"), DataType:=xlDelimited, _
TextQualifier:=xlDoubleQuote, _
ConsecutiveDelimiter:=False, _
Tab:=False, Semicolon:=False, _
Comma:=False, Space:=False, _
Other:=True, OtherChar:="|"
Do While I < UBound(xFilesToOpen)
I = I + 1
Set xTempWb = Workbooks.Open(xFilesToOpen(I))
With xWb
xTempWb.Sheets(1).Move after:=.Sheets(.Sheets.Count)
.Worksheets(I).Columns("A:A").TextToColumns _
Destination:=Range("A1"), DataType:=xlDelimited, _
TextQualifier:=xlDoubleQuote, _
ConsecutiveDelimiter:=False, _
Tab:=False, Semicolon:=False, _
Comma:=False, Space:=False, _
Other:=True, OtherChar:=xDelimiter
End With
Loop
ExitHandler:
Application.ScreenUpdating = xScreen
Set xWb = Nothing
Set xTempWb = Nothing
Exit Sub
ErrHandler:
MsgBox Err.Description, , "Kutools for Excel"
Resume ExitHandler
End Sub
Unfortunately, after a bit more trial and error with my edited code, I haven't found a better way to optimize the process, so I'm posting the final code in response to the question.
The reduction of the amount of data is now done by a third party program and speeds up the processing sufficiently.
Option Explicit
Public Sub fileImporter()
Dim fDialog As FileDialog
Dim fPath As Variant
Dim FSO
Dim Data
Dim arr, tmp, output
Dim file, fileName As String
Dim x, y As Integer
Dim newSht As Worksheet
Application.ScreenUpdating = False
Set fDialog = Application.FileDialog(msoFileDialogFilePicker)
With fDialog
.AllowMultiSelect = True
.Title = "Please select files to import"
.Filters.Clear
.Filters.Add "VBO Files", "*.vbo" 'VBO Files are opened and handled like Text Files
If .Show = True Then
For Each fPath In .SelectedItems
Set FSO = CreateObject("Scripting.FilesystemObject")
fileName = FSO.GetFilename(fPath)
Set Data = FSO.OpentextFile(fPath)
file = Data.readall
Data.Close
arr = Split(file, vbCrLf)
ReDim output(UBound(arr), 50)
For x = 0 To UBound(arr)
tmp = Split(arr(x), " ")
For y = 0 To UBound(tmp)
output(x, y) = tmp(y)
Next
Next
Set newSht = ActiveWorkbook.Sheets.Add(after:=ActiveWorkbook.Worksheets(ActiveWorkbook.Worksheets.Count))
newSht.Name = fileName
Sheets(fileName).Range("A1").Resize(UBound(output) + 1, UBound(output, 2)) = output
Next
End If
End With
Application.ScreenUpdating = True
End Sub
Thanks for all the help and I hope this can help someone else too.

Cannot able to store value in a array - VBA

I am trying to store a filename in a array but i am getting Type mismatch error. I have changed the data type but it didn't work. Kindly help.
The code block that is throwing error,
Sub Example2()
Dim objFile,objFile1,objFolder,objFolder1 As Object
Dim splitting, counter, filename, filename1, splitting1, counter1,As Variant
Dim myarray() As Variant
For Each objFile In objFolder.Files
splitting = Split(objFile.Name, "\", 9)
counter = UBound(splitting)
filename = splitting(counter)
For Each objFile1 In objFolder1.Files
splitting1 = Split(objFile1.Name, "\", 9)
counter1 = UBound(splitting1)
filename1 = splitting1(counter1)
If srch1 = srch2 Then
ReDim Preserve myarray(UBound(myarray) + 1)
myarray() = filename1
End If
Next
Next
Get File Paths (to Array) Function
Links
Objects
FileSystemObject Object
GetFolder Method
File Object
The Code
Option Explicit
Function getFilePaths(ByVal FolderPath As String, _
Optional ByVal FirstIndex As Long = 1) _
As Variant
Dim fso As Object
Dim fsoFldr As Object
Dim fsoFile As Object
Set fso = CreateObject("Scripting.FileSystemObject")
Set fsoFldr = fso.GetFolder(FolderPath)
Dim LastIndex As Long
LastIndex = FirstIndex - 1
Dim Data() As Variant
For Each fsoFile In fsoFldr.Files
LastIndex = LastIndex + 1
ReDim Preserve Data(FirstIndex To LastIndex)
Data(LastIndex) = fsoFile.Path ' or .Name, .ParentFolder ...
Next fsoFile
getFilePaths = Data
End Function
Sub TESTgetFilePath()
' Define Folder Path ('fPath').
Const fPath As String = "F:\Test\2020"
' Populate File Paths Array ('Data').
Dim Data As Variant
Data = getFilePaths(fPath)
' Validate File Paths Array.
If IsEmpty(Data) Then
MsgBox "No files found.", vbCritical, "Fail"
Exit Sub
End If
' Write title to the Immediate window (CTRL+G).
Debug.Print "The List"
' Write values from File Paths Array to a String ('Result').
Dim Result As String
Result = Join(Data, vbLf)
' Write file paths to the Immediate window (CTRL+G).
Debug.Print Result
End Sub
EDIT 1:
Sub Example2()
Const FolderPath As String = "C:\Test"
Dim fso As Object
Dim objFolder As Object
Dim objFile As Object
Set fso = CreateObject("Scripting.FileSystemObject")
Set objFolder = fso.GetFolder(FolderPath)
Dim LastIndex As Long
LastIndex = -1
Dim MyArray() As Variant
For Each objFile In objFolder.Files
LastIndex = LastIndex + 1
ReDim Preserve MyArray(LastIndex)
MyArray(LastIndex) = objFile.Name
Next objFile
Dim n As Long
For n = LBound(MyArray) To UBound(MyArray)
Debug.Print n, MyArray(n)
Next n
End Sub
EDIT 2:
Sub Example3()
' For a fileformat aaa-bbb-rev*.*, where 'rev' is to be tested if greater.
' Two hyphens only.
Const FolderPath As String = "F:\Test\2020\64568450"
Const fSep As String = "-"
Dim pSep As String
pSep = Application.PathSeparator
Dim fso As Object
Dim objFolder As Object
Dim objFile As Object
Set fso = CreateObject("Scripting.FileSystemObject")
Set objFolder = fso.GetFolder(FolderPath)
Dim dict As Object
Set dict = CreateObject("Scripting.Dictionary")
Dim FileParts As Variant ' An array containing the split file name.
Dim fName As String ' File part before the 2nd hyphen (minus) '-'
Dim fRevision As String ' File part after the 2nd hyphen (minus) '-'
Dim LastIndex As Long
LastIndex = -1
Dim MyArray() As Variant
' Write file paths to array.
For Each objFile In objFolder.Files
FileParts = Split(objFile.Name, fSep)
fName = FileParts(0) & fSep & FileParts(1)
fRevision = FileParts(2)
If Not dict.Exists(fName) Then
dict(fName) = fRevision
Else
LastIndex = LastIndex + 1
ReDim Preserve MyArray(LastIndex)
If dict(fName) < fRevision Then
MyArray(LastIndex) = FolderPath & pSep & fName _
& fSep & fRevision
dict(fName) = fRevision
Else
MyArray(LastIndex) = objFile.Path
End If
End If
Next objFile
' Now 'MyArray' contains the list of file paths of the files to be moved.
Dim n As Long
For n = LBound(MyArray) To UBound(MyArray)
Debug.Print n, MyArray(n)
Next n
End Sub
Arrays in VBA can be either static or dynamic.
A static array is declared with a fixed size:
Dim myStaticArr(10) As String
declares an array with a fixed number of members (usually 11 as the lower index starts at 0, but you can overwrite this).
If you want to be sure about the lower index, you can specify
Dim myStaticArr(1 to 10) As String
Now you have 10 elements (from 1 to 10).
Similar, a multidimensional array can be defined
Dim myStaticArr3D(1 to 10, 1 to 5, 1 to 8) As String
Now you have an array with 10 * 5 * 8 members.
All of these arrays have in common that you need to declare at compile time the size of the array. The VBA compiler will reserve the necessary amount of memory and you cannot resize it.
If you don't know at compile time how large your array will be, you can declare it as dynamic array (as you do)
Dim myDynamicArr() as String
This reserves no memory at all. Before you can write something into the array, you need to tell VBA how big the array will be. This is done using the Redim statement. Easiest form:
Redim myDynamicArr(1 to 10) as String
Usually, this is done after calculating the size needed, so you will usually find the Redim having a variable that was used to calculate the needed size:
Redim myDynamicArr(1 to sizeNeeded) as String
Now there are cases where you find at runtime that the needed size is too small. You can issue another Redim to increase the size - but as you want to keep the content of the array, you specify the option Preserve:
Redim Preserve myDynamicArr(1 to 2*sizeNeeded) as String
This will double the size and keep the content of the first members (omitting the Preserve option will double the size but the content of the existing members will get lost).
To get the current size of an array, you can use the functions LBound and UBound. This can be used on static and dynamic arrays:
Dim myStaticArr(5 to 99) As String
Debug.Print LBound(myStaticArr), UBound(myStaticArr)
>> 5 99
Dim myDynamicArr() As String
ReDim myDynamicArr(1 to 20)
Debug.Print LBound(myDynamicArr), UBound(myDynamicArr)
>> 1 20
However, if you have a dynamic array and you never assigned memory to it, the functions LBound and UBound will throw a runtime error 9 "Subscript out of range"
Now what you want to do is to increase the size of the array by 1 every time you find a new value. You achieve this with
ReDim Preserve myarray(UBound(myarray) + 1)
which will look to the current size of the array using the UBound-function and resize it by 1, preserving its contents. That's fine, except for the fact that the very first time this statement is hit, the size of the array is undefined.
The easiest way to handle this is to use a variable that keeps track of your array size:
Dim myArray() as String, myArraySize as Long
(...)
myArraySize = myArraySize + 1
ReDim Preserve myArray(1 to myArraySize)
myarray(myArraySize) = filename1
One remark: ReDim Preserve is a rather expensive command. If you are dealing with a few entries, this doesn't matter, but if you are dealing with 100s or 1000s of elements, you should consider to use a Collection.
You should index to the array to set the value after Redim:
ReDim Preserve myarray(UBound(myarray) + 1)
myarray(ubound(myarray)) = filename1
VBA arrays are so finicky and frustrating. I would add items to a string and after then split it to an array:
Dim strArray As String
strArray = ""
REM .....
strArray = strArray + filename1 + ","
REM .....
myarray = Split(strArray,",")

Excel VBA: array of Sheets, Copy from one Workbook to another

I'm trying to write a simple VBA Subroutine that:
creates a new workbook in the same directory of the excel file that contains the code ("original file" from here onward)
saves the new workbook as _export.xlsx
copies some predefined sheets from the original file to the "*_export" one.
This is what I've got at the time being:
Sub export()
Dim myPath, folderPath, fileName, exportFileFullPath As String
Dim arrayOfSheetsToCopy As Variant
folderPath = Application.ActiveWorkbook.Path
fullPath = Application.ActiveWorkbook.FullName
fileName = Replace(Application.ActiveWorkbook.Name, ".xlsm", "")
exportFileFullPath = folderPath & "\" & fileName & "_export.xlsx"
Workbooks.Add
ActiveWorkbook.SaveAs fileName:=exportFileFullPath, FileFormat:=xlOpenXMLWorkbook, CreateBackup:=False
arrayOfSheetsToCopy = Array("originalSheet1", "originalSheet2", "originalSheet3")
Workbooks(fullPath).Sheets(arrayOfSheetsToCopy).Copy After:=Workbooks(exportFileFullPath).Sheets(Sheets.Count)
End Sub
The code seems to run until a "Subscript out of range" error at Sheets(arrayOfSheetsToCopy).Copy...
Initially I though to some kind of syntax error in the definition of the Array of Sheets, so I tried to write a separate .Copy instruction for each Sheet. The same code interrupts at the same point with the same error.
Any idea? Thank you!
Workbook.FullName does not return a valid argument for the Workbooks collection.
You can test this by running ?Workbooks(ActiveWorkbook.FullName).FullName in the Immediate Window - it will error. On the other hand, Workbook.Name does work, so ?Workbooks(ActiveWorkbook.Name).FullName will not error. In other words Workbooks("C:\Users\fabbius\Documents\SomeFile.xlsx") is not valid, while Workbooks("SomeFile.xlsx") is valid, so long as a file by that name is open.
However, I fail to see the benefit of using FullName over using properly defined Workbook Objects:
Sub export()
Dim exportFileFullPath As String, arrayOfSheetsToCopy As Variant
Dim wsExportFrom As Workbook, wsExportTo As Workbook
Set wsExportFrom = ActiveWorkbook
Set wsExportTo = Workbooks.Add
exportFileFullPath = Replace(wsExportFrom.FullName, ".xlsm", "_export.xlsx", Len(wsExportFrom.Path))
'The Len() is in case the File Path contains ".xlsm" for some reason
wsExportTo.SaveAs fileName:=exportFileFullPath, FileFormat:=xlOpenXMLWorkbook, CreateBackup:=False
arrayOfSheetsToCopy = Array("originalSheet1", "originalSheet2", "originalSheet3")
wsExportFrom.Sheets(arrayOfSheetsToCopy).Copy after:=wsExportTo.Sheets(wsExportTo.Sheets.Count)
End Sub
Of course, if this Macro is being run from the workbook you intend to export from, then With and ThisWorkbook make things even simpler:
Sub export()
Dim exportFileFullPath As String, arrayOfSheetsToCopy As Variant
exportFileFullPath = Replace(ThisWorkbook.FullName, ".xlsm", "_export.xlsx", Len(ThisWorkbook.Path))
'The Len() is in case the File Path contains ".xlsm" for some reason
arrayOfSheetsToCopy = Array("originalSheet1", "originalSheet2", "originalSheet3")
With Workbooks.Add
.SaveAs fileName:=exportFileFullPath, FileFormat:=xlOpenXMLWorkbook, CreateBackup:=False
ThisWorkbook.Sheets(arrayOfSheetsToCopy).Copy after:=.Sheets(.Sheets.Count)
End With
End Sub
Final note: You are saving the file before you add the worksheets to it. Should those lines be the other way around?
This works for me
Sub export()
Dim myPath, folderPath, fileName, exportFileFullPath As String
Dim arrayOfSheetsToCopy As Variant
Dim sht As Worksheet
Dim newWorkBook As Workbook
folderPath = Application.ActiveWorkbook.Path
fullPath = Application.ActiveWorkbook.FullName
fileName = Replace(Application.ActiveWorkbook.Name, ".xlsm", "")
fileName = Replace(fileName, ".xlsx", "")
exportFileFullPath = folderPath & "\" & fileName & "_export.xlsx"
Set newWorkBook = Workbooks.Add
Call newWorkBook.SaveAs(fileName:=exportFileFullPath, FileFormat:=xlOpenXMLWorkbook, CreateBackup:=False)
For Each sht In ThisWorkbook.Sheets
Call sht.Copy(after:=newWorkBook.Sheets(Sheets.Count))
Next sht
Call newWorkBook.Close(saveChanges:=True)
End Sub
or if you want to use predefined sheetnames
For Each sheetName In Array("originalSheet1", "originalSheet2", "originalSheet3")
Call ThisWorkbook.Sheets(sheetName).Copy(after:=newWorkBook.Sheets(Sheets.Count))
Next sheetName

VBA failing with array or user defined type expected

I have some code I am moving from VB.NET to VBA which has worked in the .NET world quite well. I have successfully moved almost all of the code into the VBA world with one exception thus far. Here is much of the code in question and all the variable declarations`
Dim vault As IEdmVault14
Dim eFile As IEdmFile9
Dim eFolder As IEdmFolder7
Dim pos As IEdmPos5
Dim Pathlist As EdmStrLst5
Dim parentFolder As IEdmFolder5
Dim vaultName As String
Dim filePath As String
Dim AssyName As String
Dim LoggedIn As Boolean
Set EdmVault5 = New EdmVault5
Set vault = New EdmVault5Dim fso As New FileSystemObject
Dim sw As TextStream
Set sw = fso.CreateTextFile("c:\temp\" & AssyName & ".txt")
'-----------------------------GET COLUMN HEADERS
Dim columns() As EdmBomColumn
BOM.GetColumns columns
Dim header As String
header = "LEVEL" & vbTab
Dim column As EdmBomColumn
For i = 0 To UBound(columns)
header = header & columns(i).mbsCaption & vbTab
Next
sw.writeline (header)
'-----------------------------Bom.READ EACH BOM ROW
Dim rows As Object
Dim row As IEdmBomCell
BOM.GetRows (rows)
For i = 0 To UBound(rows)
If IsNothing(row) Then Exit For
Dim rowString As String
Set rowString = row.GetTreeLevel.ToString & vbTab
Dim varVal As String
varVal = ""
For i = 0 To UBound(columns)
row.GetVar(column.mlVariableID, column.meType, varVal, Nothing, Nothing, Nothing)
If IsNothing(varVal) Then varVal = ""
rowString = rowString & varVal & vbTab
Next
'-----------------------------WRITE THE ROW TO THE FILE
sw.writeline (rowString)
Next
sw.Close
`
The array error occurs at BOM.GetRows (rows). I am stuck on what the issue could be. This error code does not occur in VB.NET but .NET does warn that Variable 'rows' is passed by reference before it has been assigned a value. A null reference exception could result at runtime. I am not clear on how that translates into VBA if at all.
If anyone could shed some light on this it would be helpful I'm sure.
If you have a method signature (or function signature or whatever) that requires an array, then you have to Dim the variable you pass in as an array.
Public Sub test()
Dim x As Variant
Debug.Assert Not IsArray(x)
x = Array(1, 2)
Debug.Assert IsArray(x)
GetStuff x 'this fails
Stop
End Sub
Public Function GetStuff(a() As Variant) As Double
GetStuff = 1
End Function
Even though a Variant can hold an array, it doesn't pass the IsArray test just by declaring it. If I assign an array to it, it passes IsArray, but I still can't use it as an argument to function that requires an array. x is a Variant array and a() is an array of Variants. So the above code still won't compile.
It sounds from your comments that you got it sorted, but I thought I'd throw a little more information out there for posterity.

How Can I Make Sure I Only Open Text Files When Working With FSO - VBA

Currently have a working script that uses FSO, but it also opens .xlsm files within my working directory. I would like to to only open .txt files.
I found this code that should work, however I can't figure out how to apply it to my situation:
Sub test()
' Loop thru all files in the folder
folder = ActiveWorkbook.path
path = folder & "\*.txt"
Filename = Dir(path)
Do While Filename <> ""
'insert other functions here
Loop
End Sub
My Code (Works, but also opens .xlsm files, which I don't want it to do):
Option Explicit
Sub Initialize_barcode_lookup_Array_test()
Dim fso As FileSystemObject
Dim folder As String, path As String, count_txt_files As Long, Filename As String
Dim folder2 As folder
Dim file As file
Dim FileText As TextStream
Dim TextLine As String
Dim Items() As String
Dim ShippingPlanArray() As String
Dim i As Long, j As Long, k As Long
Dim cl As Range
Dim fName
Dim row As Long, column As Long
Dim shipping_plan As Long 'Number of shipping plans text files imported
Dim barcode_Lookup() As String
Dim lastRow As Long
Dim longest_lastRow As Long
Dim counter As Long
Dim FNSKU_Input As String
'<<<< Creating FSO Object >>>>>
'Define longest_lastRow
longest_lastRow = 0
'Define i (References the text file open)
i = 0
' Get a FileSystem object
Set fso = New FileSystemObject
' get the directory you want
Set folder2 = fso.GetFolder(ActiveWorkbook.path)
' Loop only while the files being opened are .txt files:
For Each file In folder2.Files
row = 0
column = 0
Set FileText = file.OpenAsTextStream(ForReading)
Do Until FileText.AtEndOfStream
fName = FileText.ReadLine
'Parse data by tabs (text-tab delimited) into Items() array
Items() = Split(fName, vbTab)
' Redimension Preserve the ShippingPlanArray()
' NOTE: You can only Redimension preserve the last dimension of a multi-dimensional array
' (In this case: row)
ReDim Preserve ShippingPlanArray(9, row)
'Read Data into an Array Variable
For column = LBound(Items) To UBound(Items)
'MsgBox Items(column)
ShippingPlanArray(column, row) = Items(column)
Next column
row = row + 1
Loop
Next file
End Sub
I don't know if fso support an overloaded method for GetFolder where you can specify the pattern. If it does, use that i.e. GetFolder(Path, "*.txt"). If it doesn't, can you not just add a simple condition to check the file extension in your 'for each' loop and only process the ones that ends in '.txt'.
Update:
Try this:
For Each file In folder2.Files
Dim extension As String
extension = LCase(Mid$(file, InStrRev(file, ".")))
If extension = ".txt" Then
Debug.Print "TEST"
End If
Next
I've tested it and it works as expected.

Resources