Workbooks, Worksheets, and Arrays (Chapters 8 (132-145), 9 of Albright Ed 3) (Chapters 8 (149-163), 9 of Albright Ed 4) (Chapter 13 (Section 6) of Albright Ed 3 & 4) Kipp Martin January 25, 2012
Excel Files Files used in this lecture: workbooksdemo.xlsm fileio.xlsm arrays.xlsm
Outline Workbooks and Worksheets Text Files Arrays
Workbooks, Worksheet, and Arrays Motivation: Data not always in a single worksheet! Data that is needed for a model may reside in multiple worksheets in a workbook. (This lecture) Data that is needed for a model may reside in multiple workbooks. (This lecture) Data that is needed for a model may reside in text files. (This lecure) Data that is needed for a model may reside in XML files. (later) Data that is needed for a model may reside on the Web. (later) Data that is needed for a model may reside in relational data bases (Access, SQL Server, Oracle, DB2) (later)
Workbooks, Worksheet, and Arrays Central Idea: Work with a collection of objects. In this case we work with Workbooks and Worksheets. Note the plural. Class hierarchy is critical!
Workbooks, Worksheet, and Arrays Here is how I might refer to a range: Workbooks("CustData").Worksheets("Cust1").Range("Sales") The hierarchy is: A Workbook CustData A Worksheet Cust1 A Range Sales Note: for this to work the workbook CustData must be open.
Workbooks, Worksheet, and Arrays Useful Task One: Looping over the Workbooks collection Dim wb As Workbook For Each wb In Application.Workbooks MsgBox wb.name Next Useful Task Two: Looping over the Worksheets collection Dim ws As Worksheet For Each ws In ActiveWorkbook.Worksheets MsgBox ws.name Next
Workbooks, Worksheet, and Arrays Useful Task Three: Looping over all Ranges (range names are members of the Workbook, not individual worksheets) For Each wb In Application.Workbooks wb.activate MsgBox wb.name numranges = wb.names.count For i = 1 To numranges Debug.Print ActiveWorkbook.Names.Item(i).Name Next i Next
Workbooks, Worksheet, and Arrays Useful Task Four: Open a closed Workbook. You may want your application to open a closed workbook. Assume the Workbook studentscores.xlsm is in the C directory root. Sub OpenCloseWorkbooks() Dim filename As String filename = "C:\studentscores.xlsm" Workbooks.Open filename Now close it Workbooks("studentscores.xlsm").Close End Sub
Workbooks, Worksheet, and Arrays Useful Task Five: Saving a workbook under a different name or in a different directory. With ActiveWorkbook just save where it is under current name.save do a save as.saveas filename:="c:\test.xlsm" End With Useful Task Six: Determining which directory a workbook is in. MsgBox ThisWorkbook.Path
Workbooks, Worksheet, and Arrays Useful Task Seven: Hiding a worksheet. Why do this? For Each wb In Application.Workbooks If wb.name = "workbooksdemo.xlsm" Then For Each ws In wb.worksheets Set ws = Worksheets(ws.Name) If ws.name = "Sheet2" Then _ ws.visible = xlsheethidden Next End If Next
Workbooks, Worksheet, and Arrays 12 Useful Task Eight: Executing code when a workbook opens. See Section 10.7 of Albright. Basic Idea: It is often useful to execute code when a workbook is opened (this is an event). Perhaps opening a specific worksheet. Create a procedure named Workbook Open() and put it in the ThisWorkbook section of the project. Private Sub Workbook_Open() MsgBox "Hello World" Worksheets("Sheet1").Activate End Sub See also Workbook BeforeClose
Workbooks, Worksheet, and Arrays Useful Task Eight (Continued): Executing code when for all Workbooks when they open. See http://www.rondebruin.nl/personal.htm for information on putting a macro in your PERSONAL.XLSB workbook. Private Sub Workbook_Open() Application.OnKey "{F5}", "" End Sub
Text Files We need to read and write simple text files. First, how to write a simple text file. We write the information from a range to a text file. Step1: Open the file Step 2: Write the range information to a string Step 3: Write the string to the file Step 4: Close the file
Step 1: Open the file Text Files testfile = "C:\temp\data.txt" Mac OS X testfile = "MacIntosh HD:Users:kmartin:temp:data.txt" Open testfile For Output As #1 Step 2: Write range information to the string. filedata = "" For I = 1 To numrows For J = 1 To numcols filedata = filedata & rng.cells(i, J) & vbtab Next J go to a new line filedata = filedata & vbcrlf Next I
Text Files Step 2 (Continued): Write range information to the string. Note the use of: vbtab vbcrlf Step 3: Write the string to the file Print #1, filedata Step 4: Close the file Close #1
Text Files Now reverse the process and read a text file. Step1: Open the file Step 2: Read the information line by line into a string Step 3: Parse (argle bargle) the string Step 4: Close the file
Text Files Step1: Open the file testfile = "C:\temp\data.txt" Mac OS X testfile = "MacIntosh HD:Users:kmartin:temp:data.txt" Open testfile For Input As #1 Step 2: Read the information line by line into a string Do Until EOF(1) Line Input #1, dataline Debug.Print dataline Loop
Text Files Step 3: Parse the string not illustrated here. See Section 13.6. Not an easy task in general. Step 4: Close the file. Close #1
Text Files Text files are great: platform independent more secure over a network most software packages (word processors, spreadsheet, database) read and write text files. truly the lowest common denominator The Bad: hard to express complicated data structures in text files The Solution: XML
Arrays There is an Array construct in VBA. This construct is similar to a range in an Excel worksheet. In fact, you could probably get by without arrays and just work with Range objects. But arrays make your life much easier. And faster! Arrays can make the code much cleaner. You do not constantly have to work with the Cells or Offset properties. Arrays makes it very easy to pass back and forth large amounts of data between functions and procedures using an array as an argument. You don t have to constantly figure out where you want to be in a range. Arrays are very convenient for storing temporary information that you don t want to write out to an Excel Range. Using an array index is a very fast and efficient way to refer to a number, string, or object in memory location.
Arrays 22 An array should be declared like any other variable. An example declaration: Dim x(10) As Double This allocates a double array with 10 as the largest subscript value. VBA knows that this is an array because of the (10) as part of the x variable. By default, Excel VBA uses zero-based counting. This means that x(0) is the first element and x(10) is the last element. In general, x(i) indexes element i + 1 with zero-based counting.
Arrays With Option Base set to 0 the statement Dim x(100) As Double will create a an array with 101 elements. This is NOT true in C, C++, or Java. What the author says about this on the bottom of page 156 (about employee) is wrong.
Arrays Zero-based counting is also used in the C, C++, and Java programming languages. It is relatively recent addition to VBA. Many people find this confusing. There are two ways to specify one-based counting which is what a lot of people are used to using. Specify the start and end of the array. For example: Dim x(1 To 10) Double At the start of the module declare one-based counting as the default Option Base 1
Arrays By default, a statement such as Dim x(10) As Double will initialize all elements of the x array to zero. You can also do something like this: Dim z As Variant z = Array(1.2, 3, 5, 8.9, 9, 11) This initializes z to have the six elements given above.
Arrays There is a VBA function UBound that takes an array as the argument and returns the largest subscript, not the number of elements in the array. Consider the following code: Dim x(10) As Double Debug.Print UBound(x) This code will produce the value of 10 for the size of the array regardless of 0 or 1 based counting. There is a VBA function LBound that takes an array as the argument and returns the smallest subscript. The number of elements in the array is: UBound( x) - LBound( x) + 1
Arrays You can declare multi-dimensional arrays. The following example calculates the sum product of two tables. (Assume one-based counting.) Dim table1(5, 10) As Double Dim table2(5, 10) As Double Dim i As Double, j As Double Dim total As Double For i = 1 To 5 For j = 1 To 10 total = total + table1(i, j)*table2(i, j) Next j Next i
Arrays You can declare dynamic arrays. Use the ReDim statement once you know the size of the array. Read a range of midterm scores into an array Define a dynamic array Dim midterms() As Double Dim rng As Range With Range("A1") Set rng = Range(.Offset(1, 4), _.Offset(1, 4).End(xlDown)) End With Dim numrows As Integer numrows = rng.rows.count ReDim midterms(numrows) Dim i As Integer For i = 1 To numrows midterms(i) = rng.cells(i) Next i
Arrays You can ReDim arrays multiple times. If you do this you may wish to use the Preserve keyword. Sub Demo_Preserve() Demo use of ReDim and Preserve Dim i As Integer Dim x() As Double ReDim x(2) x(1) = 1 x(2) = 2 ReDim x(3) try with and without statement below ReDim Preserve x(3) x(3) = 3 For i = 1 To 3 Debug.Print x(i) Next i End Sub
Arrays 30 See the procedures Total Sales1() and Total Sales2() for a good illustration of using arrays. In these procedures we find unique sku numbers and then aggregate sales for each sku. The Total Sales2() macro illustrates use of IsEmpty().
Using IsEmpty(): Arrays Do While IsEmpty(rng.Cells(i, 1)) = False isunique = True For j = 1 To numunique see if this sku is already there If rng.cells(i, 1) = unique_sku(j) Then get out of loop, not a unique sku update the total total(j) = total(j) + rng.cells(i, 3).. other logic. i = i + 1 Loop