Suggestions |
Example File
ArrayList
|
1. What is an ArrayList ?
2. Why using an ArrayList ?
3. The origin of the ArrayList
4. Create an ArrayList 4.1 Early binding 4.1.1 Local scope 4.1.2 Private scope 4.1.3 Public scope 4.2 Late binding 4.2.1 Local scope 4.2.2 Private scope 4.2.3 Public scope 5. Populate an ArrayList 5.1 Separate elements 5.1.1 Method Add 5.1.2 Method Insert 5.2 A group of elements 5.2.1 Method AddRange 5.2.2 Method InsertRange 5.3 Kinds of Items 5.3.1 empty string 5.3.2 normal string 5.3.3 not-printable character 5.3.4 number 5.3.5 date 5.3.6 1-dimensional array 5.3.7 multi-dimensional array 5.3.8 object 5.3.9 controls in Userform 5.3.10 ActiveX controls 5.3.11 all worksheets 6. The size of an ArrayList 7. Check an item's presence 8. Check an item's position 9. Read elements 9.1 one element 9.2 adjacent elements 9.3 all element 10 Change an element 11 Sorting elements 11.1 Ascending sorting 11.2 Descending sorting 11.3 Sorting Arrays 12 Delete elements 12.1 one element by contents 12.2 one element by index 12.3 adjacent elements 12.4 all elements 13. Copy an ArrayList 14. Key words |
In VBA an ArrayList is a 'container' object: you can store all kinds of information in it: numbers, strings, dates, arrays, ranges, variables and objects. VBA has several comparable options to store information: - a dictionary - a collection - an array variable - an ActiveX ComboBox - an ActiveX ListBox - a Userform control ComboBox - a Userform control ListBox - a sortedlist Which one to use is dependent of the goal you want to achieve. In this page I don't want to make an extensive comparison of all methods. I confine the information to the possibilities of the ArrayList. There's 1 outstanding difference to other storing methods like Collection, Array or Dictionary; the arraylist has a built in sorting method. As far as I know it's the only sorting method in VBA (apart from the 'sortedlist'). This sorting method is also very fast. It can be a valid consideration to use an ArrayList. 2. The purpose of an ArrayList You can collect data from several sources into an ArrayList to employ them: analysing, comparing, calculating, filtering, etc.Instead of manipulating data in an Excel-worksheet, a Word Document or a Powerpoint presentation, you can use the Arraylist to do this in memory. No screenupdating, no redundant calculations nor event procedures are being performed so the data processing can be very fast. 3. The origin of the ArrayList The ArrayList is not part of the standard VBA-library.The ArrayList is part of the library 'System.Collections'. You'll find it in the file ....\WINDOWS\Microsoft.NET\Framework\v4.0.30319\mscorlib.tlb or in a comparable directory. 3.1 'Early Binding': link to the library manually In the VBEditor you can establish a direct link to this library by checking mscorlib.tlb in the list of References (Tools/References..)A file in which the direct link exists will store the link and activate after distribution. 3.2 'Early Binding': link to the library in VBA In VBA you can also establish the reference using the .AddFromFile method or the .AddFromGuid method.ThisWorkbook.VBProject.References.AddFromFile "C:\WINDOWS\Microsoft.NET\Framework\v4.0.30319\mscorlib.tlb" ThisWorkbook.VBProject.References.AddFromguid "{BED7F4EA-1A96-11D2-8F08-00A0C9A6186D}", 2, 4 3.3 'Late Binding': link to the library in VBA You don't need an active reference (('early binding') to make use of the ArrayList.You can also decide to create the link at the moment you need it ('late binding'). This is the code to establish a new ArrayList: CreateObject("System.Collections.ArrayList") Which one to use is dependent of: - whether you want to use 'early binding' or 'late binding' - which 'scope' you want to deploy: the availability of the ArrayList in 1 procedure or several procedures: 'local scope', 'private scope', 'public scope' 4.1 Established reference to the library: 'early binding' If you only need the ArrayList in 1 macro or function you can restrict the definition of the ArrayList to that procedure.without the use of a variable: With new ArrayList .Add "aa1"
end withdim a_00 as New ArrayList a_00.Add "aa1" set a_00 = New ArrayList a_00.add "aa1" dim a_00 as Object set a_00= new ArrayList a_00.add "aa1" dim a_00 as ArrayList set a_00= new ArrayList a_00.add "aa1" In that case the use of a variable is obligatory. The variable has to be declared in the declaration area of the codemodule. 4.1.2.1 Variable declaration and assigning a new instance simultaneously If you use this code in the declaration areaDim a_00 As New ArrayList Private a_00 as New ArrayList 4.1.2.2 Variable declaration and assigning a new instance separately The declaration of the variable must be part of the declaration area in the codemodule.The datatype of the variabele can be Object, ArrayList or Variant. Since Variant is the default datatype you can refrain from explicitly specifying the datatype. In the declaration area of the codemodule Dim a_00 Private a_00 Dim a_00 as Variant Private a_00 as Variant Dim a_00 as Object Private a_00 as Object Dim a_00 as ArrayList Private a_00 as ArrayList Set a_00 = New ArrayList In that case the use of a variable is obligatory. The variable has to be declared in the declaration area of a/any macromodule. 4.1.3.1 Variable declaration and assigning a new instance simultaneously In the declaration area of the codemodule of a macromodulePublic a_00 As New ArrayList 4.1.3.2 Variable declaration and assigning a new instance separately The declaration of the variable must be part of the declaration area in the codemodule of a macromodule.The datatype of the variabele can be Object, ArrayList or Variant. Since Variant is the default datatype you can refrain from explicitly specifying the datatype. In the declaration area of the codemodule of a macromodule: Public a_00 Public a_00 as Variant Public a_00 as Object Public a_00 as ArrayList Assign a new instance of the Arraylist to the declared variable. Since the instance of an ArrayList is an Object you need to use 'Set' to assign the instance to the variable. Set a_00 = new ArrayList 4.2 Without a reference to the library: 'late binding' If you only need the ArrayList in 1 macro or function you can restrict the definition of the ArrayList to that procedure.without the use of a variable: With CreateObject("System.Collections.ArrayList") .Add "aa1"
end withset a_00 = CreateObject("System.Collections.ArrayList") a_00.Add "aa1" You can use the Class Identification Number CLSID in combination with the GetObject method. Every CLSID is stored in the Windows Registry. without a variable: With GetObject("New:{6896B49D-7AFB-34DC-934E-5ADD38EEEE39}") ' sortedlist
.Add "aa1"
End Withset a_00 = GetObject("New:{6896B49D-7AFB-34DC-934E-5ADD38EEEE39}") ' sortedlist a_00.Add "aa1" In that case the use of a variable is obligatory. The variable has to be declared in the declaration area of the codemodule. The datatype of the variabele can be Object or Variant. Since there's no reference yet to the library you can't use the datatype 'ArrayList'. Since Variant is the default datatype you can refrain from explicitly specifying the datatype. In a procedure (macro or function) you have to assign a new instance of the ArrayList to the previously declared variable. In the declaration area of the codemodule Dim a_00 Private a_00 Dim a_00 as Variant Private a_00 as Variant Dim a_00 as Object Private a_00 as Object Set a_00 = CreateObject("System.Collections.ArrayList") In that case the use of a variable is obligatory. The variable has to be declared in the declaration area of a/any macromodule. The datatype of the variabele can be Object or Variant. Since there's no reference yet to the library you can't use the datatype 'ArrayList'. Since Variant is the default datatype you can refrain from explicitly specifying the datatype. In a procedure (macro or function) you have to assign a new instance of the ArrayList to the previously declared variable. In the declaration area of the codemodule of a macromodule Public a_00 Public a_00 as object Public a_00 as object set a_00 = CreateObject("System.Collections.ArrayList") In this page I will only use the With ... End With method in a 'late binding' context. The content of an item can be various: numbers, strings, dates, arrays, ranges, variables, collections, dictionaries, an empty string, nothing or objects. You can assign items to an ArrayList separately or as a group of items. You also can indicate where the new items should be added into the ArrayList. The method .Add always adds a new element after the last one of the ArrayList. With CreateObject("System.Collections.ArrayList") .Add "content"
.Add New Collection
End With.Add 123 .Add Date .Add Array("red", "white", "green") Take notice of the fact that the first element in the ArrayList has index number 0. With CreateObject("System.Collections.ArrayList") .Add "aa1"
End With.Add "aa2" .Add "aa4" .Insert 2,"aa3" That group of items has to be prepared into a special 'Array'. To that purpose you need the us of a '.Queue', that is also part of the same library that contains the ArrayList. So first you will have to populate a '.Queue' with the items you want to add as a group. The instruction to list them in a Queue is '.Enqueue' (cfr. .Add in the ArrayList). In the next step this Queue can be added to the ArrayList with the method '.AddRange'. You can add the Queue at the rear (.AddRange) or at an indicated index number (.InsertRange). The method .AddRange always adds the group of items at the rear of the ArrayList. Set q_00 = CreateObject("System.Collections.Queue") q_00.Enqueue "one" q_00.Enqueue "two" With CreateObject("System.Collections.ArrayList") .Add "aa1"
End With.Add "aa2" .Add "aa3" .AddRange q_00 Take notice that the first element in the ArrayList has index number 0. Set q_00 = CreateObject("System.Collections.Queue") q_00.Enqueue "four" q_00.Enqueue "five" With CreateObject("System.Collections.ArrayList") .Add "aa1"
End With.Add "aa2" .Add "aa3" .InsertRange 1, q_00 Items can be of various types: numbers, strings, dates, arrays, ranges, variables, collections, dictionaries, an empty string, nothing or objects. Some illustration of several types of elements: With CreateObject("System.Collections.ArrayList")
.Add vbNullString
End With.Add "" With CreateObject("System.Collections.ArrayList")
.Add "abcde"
End With.Insert 0, "fghi" 5.3.3 a non printable character With CreateObject("System.Collections.ArrayList")
.Add vbTab
End With.Insert 0, vbLf With CreateObject("System.Collections.ArrayList")
.Add 12345
End With.Insert 1, RGB(23, 45, 678) ' typename: Integer
' typename: Long With CreateObject("System.Collections.ArrayList")
.Add Date
End With.Add CDate("23-04-2012") .Insert 2, DateSerial(2013, 10, 12) 5.3.6 a 1-dimensional Array (typename: Variant()) With CreateObject("System.Collections.ArrayList")
.Add Array("aa1", "aa2", "aa3")
End With.Add Split("bb1_cc1_dd1", "_") .Insert 1,Array("aa1", "aa2", "aa3") 5.3.7 a multi-dimensional Array (typename: Variant()) With CreateObject("System.Collections.ArrayList")
ReDim sn(6, 10)
End With.Add sn .Insert 1, sn With CreateObject("System.Collections.ArrayList")
.Add Range("A1:K10")
End With.Insert 1, Range("A1:K10") With CreateObject("System.Collections.ArrayList")
For Each it In Controls
End With.Add it
Next5.3.10 all ActiveX-controls in a worksheet With CreateObject("System.Collections.ArrayList")
For Each it In Sheets("sheet1").OLEObjects
End With.Add it
Next5.3.11 all sheets in a workbook With CreateObject("System.Collections.ArrayList")
For Each sh In Sheets
End With.Add sh
Next.Insert 1, sh With CreateObject("System.Collections.ArrayList")
For Each sh In Sheets
End With.Add sh
Next.Insert 1, sh MsgBox .Count 7. Check the existence of an item An ArrayList can contain the same value an unlimited time.The property .Contains indicates whether the value is part of the ArrayList: With CreateObject("System.Collections.ArrayList") For Each it In Array("aa1", "aa2", "aa1", "aa3", "aa4", "aa2")
End WithIf Not .contains(it) Then .Add it
NextThe use of the property .Contains helps to create a list of unique values/items. See the example above. 8. Check the item's position in an ArrayList The property .IndexOf indicates the position (=index number) of an item in an ArrayList.The first argument is the value you are looking for, the second one the position from which you want to check the occurrence of the value: With CreateObject("System.Collections.ArrayList") For Each it In Array("aa1", "aa2", "aa3", "aa4", "aa5", "aa6")
End WithIf Not .contains(it) Then .Add it
Nextmsgbox .IndexOf("aa4", 0) ' 3 rd position typename: Integer
If the item doesn't exist the result is -1If the item exists the result is the index number. NB. the first index number=0. If you want to check the occurrence of an item before or from a certain index number onwards you can enter that index number as second argument. The value you are looking for will be sought in the 'sub array' starting at the indexnumber that is the second argument. If the value occurs in this 'sub array' the indexnumber of the value in the complete list will be returned. If the 'sub array' doesn't contain the value you are checking for the result will be -1. With CreateObject("System.Collections.ArrayList") For Each it In Array("aa1", "aa2", "aa3", "aa2", "aa4", "aa6")
End WithIf Not .contains(it) Then .Add it
Nextmsgbox .IndexOf("aa2", 0) msgbox .IndexOf("aa2", 1) msgbox .IndexOf("aa2", 2) msgbox .IndexOf("aa2", 3) msgbox .IndexOf("aa2", 4) msgbox .IndexOf("aa2", 5) ' position 1 ' position 1 ' position 3 ' position 3 ' position -1 ' position -1 Since identical values can occur several times you can check the last occurrence of that value in the ArrayList. That's what the property .LastIndexOf does: With CreateObject("System.Collections.ArrayList") For Each it In Array("aa1", "aa2", "aa3", "aa3", "aa2", "aa6")
End With.Add it
Nextmsgbox .LastIndexOf("aa2") 9. Reading/retrieving elements 9.1 Reading/retrieving 1 element Use the index number to retrieve an item.With CreateObject("System.Collections.ArrayList") For Each it In Array("aa1", "aa2", "aa3", "aa3", "aa2", "aa6")
End With.Add it
Next.add array("P_01","P_02","P_03","P_04") .add "last item" msgbox .Item(0) msgbox .Item(.count-1) msgbox .Item(6)(3) ' the first item
' the last item ' the 4th element in the array in the 6th item in the ArrayList. 9.2 reading/retrieving successive elements The method .GetRange starts at the index number (first argument) and read an amount (second argument) of consecutive elements from the ArrayList.Since .GetRange stores the result in another Arraylist you need the 'Set' statement to assign it to an object variable. With CreateObject("System.Collections.ArrayList") For Each it In Array("aa1", "aa2", "aa3", "aa3", "aa2", "aa6")
End With.Add it
NextSet st = .getrange(2, 4) for each it in st msgbox it
next' read 4 elements starting at index number 2 (= third item)
If you want the ArrayLIst that is the result of .GetRange to be written to a 'normal' Array you can use the method that is the subject of the next paragraph: .ToArray.In that case you don't need the 'Set' statement. With CreateObject("System.Collections.ArrayList") For Each it In Array("aa1", "aa2", "aa3", "aa3", "aa2", "aa6")
End With.Add it
Nextst = .GetRange(2, 4).ToArray msgbox Join(st,vbLf) ' read 4 elements starting at index number 2 (= third item)
9.3 reading/retrieving all elements The method .ToArray writes all elements of the ArrayList into a 1-dimensional Array.Dependent of the resulting arrays contents you can apply the methods 'join', 'filter' and 'replace'. With CreateObject("System.Collections.ArrayList") For Each it In Array("aa1", "aa2", "aa3", "aa3", "aa2", "aa6")
End With.Add it
Nextsn=.toarray MsgBox Join(sn,vbLf) Horizontally: With CreateObject("System.Collections.ArrayList") For Each it In Array("aa1", "aa2", "aa3", "aa3", "aa2", "aa6")
End With.Add it
Nextsheet1.cells(1).resize(,.count)=.toarray With CreateObject("System.Collections.ArrayList") For Each it In Array("aa1", "aa2", "aa3", "aa3", "aa2", "aa6")
End With.Add it
Nextsheet1.cells(1).resize(.count)=application.transpose(.toarray) You can write directly into the item if you use the method .Item in combination with its index number. With CreateObject("System.Collections.ArrayList") For Each it In Array("aa1", "aa2", "aa3", "aa3", "aa2", "aa6")
End With.Add it
Next.item(3)="aa20" .item(4)=12 .item(5)=Date .item(2)=array(1,2,3,4,5) 11.1 ascending sorting elements The method .Sort sorts all elements in the ArrayList in ascending order.Prerequisite of course is that any sorting is possible: the values of the elements must contain strings or numbers (no arrays or objects e.g.). The ArrayList evaluates whether the sorting should be text based or number based. A mixture of datatypes generates an error. sorting strings With CreateObject("System.Collections.ArrayList")
For Each it In Array("aa1", "aa2", "aa3", "aa3", "aa2", "aa6")
End With .Add it
Next.Sort sn = .toarray MsgBox Join(sn, vbLf) With CreateObject("System.Collections.ArrayList")
For Each it In Array("12", "112", "2", "34", "305", "302")
End With .Add it
Next.Sort sn = .toarray MsgBox Join(sn, vbLf) With CreateObject("System.Collections.ArrayList")
For Each it In Array(12, 112, 2, 34, 305, 302)
End With .Add it
Next.Sort sn = .toarray MsgBox Join(sn, vbLf) With CreateObject("System.Collections.ArrayList")
For Each it In Array("aa1", 12, 112, "aa3", "aa2", "aa6")
End With .Add it
Next.Sort ' error
11.2 descending sorting elements The method .reverse sorts all elements in an ArrayList in descending order.Prerequisite of course is that any sorting is possible: the values of the elements must contain strings or numbers (no arrays or objects e.g.). The ArrayList evaluates whether the sorting should be text based or number based. A mixture of datatypes generates an error. sorting strings With CreateObject("System.Collections.ArrayList")
For Each it In Array("aa1", "aa2", "aa3", "aa3", "aa2", "aa6")
End With.Add it
Next.Reverse MsgBox Join(.toarray, vbLf) With CreateObject("System.Collections.ArrayList")
For Each it In Array("12", "112", "2", "34", "305", "302")
End With.Add it
Next.Reverse MsgBox Join(.toarray, vbLf) With CreateObject("System.Collections.ArrayList")
For Each it In Array(12, 112, 2, 34, 305, 302)
End With .Add it
Next.Reverse MsgBox Join(.toarray, vbLf) With CreateObject("System.Collections.ArrayList")
For Each it In Array("aa1", 12, 112, "aa3", "aa2", "aa6")
End With .Add it
Next.Reverse ' error
If an Arraylist contains Arrays you can't sort them with the .Sort. or .Reverse method.There's a simple workaround: 1. add the items of the Array's sorting 'column' in the Arraylist 2. sort the items using .Sort or .Reverse 3. put the sorted items in an Array using .ToArray 4. clear the ArrayList 5. Add the 'rows' of the original Array to the Arraylist in the order of the sorted Array Sub M_snb()
sn = Sheet1.Range("A1:G20")
End SubWith CreateObject("System.Collections.Arraylist") For j = 1 To UBound(sn)
End With.Add sn(j, 3)
Next.Sort sp = .ToArray .Clear For j = 0 To UBound(sp) For jj = 1 To UBound(sn)
NextIf sn(jj, 3) = sp(j) Then
Next.Add Application.Index(sn, jj)
End Ifsn(jj, 3) = "" Exit For For j = 0 To .Count - 1 Sheet1.Cells(j + 1, 10).Resize(, UBound(sn, 2)) = .Item(j)
Next12. delete elements from an ArrayList 12.1 delete 1 element by value The method .Remove deletes 1 element from the ArrayList.In the argument you enter the element's value. If no such element exists the method will be ignored; VBA doesn't generate an error nor a message. Delete the first item that contains the value "aa3": With CreateObject("System.Collections.ArrayList")
For Each it In Array("aa1", "aa2", "aa3", "aa3", "aa2", "aa6")
End With.Add it
Next.Remove "aa3" MsgBox Join(.ToArray,vbLf) 12.2 delete 1 element by its position (index number) in the ArrayList The method .RemoveAt deletes the element with the index number as entered in the argument of the method.If the indicated index number doesn't exist VBA will generate an error and an error message. Delete the 5th element (= index number 4) With CreateObject("System.Collections.ArrayList")
For Each it In Array("aa1", "aa2", "aa3", "aa4", "aa5", "aa6")
End With.Add it
Next.RemoveAt 4 MsgBox Join(.ToArray,vbLf) 12.3 delete successive elements The method .RemoveRange deletes a group of adjacent elements.In the first argument you enter the index number of the first item that has to be deleted, in the second argument the number of elements after the first one that have to be deleted. If any of both arguments is incorrect VBA generates an error and an error message. Delete 3 elements starting from index number 2: With CreateObject("System.Collections.ArrayList")
For Each it In Array("aa1", "aa2", "aa3", "aa3", "aa2", "aa6")
End With.Add it
Next.RemoveRange 2,4 MsgBox Join(.ToArray,vbLf) With CreateObject("System.Collections.ArrayList")
For Each it In Array("aa1", "aa2", "aa3", "aa3", "aa2", "aa6")
End With.Add it
Next.Clear MsgBox .Count 13. make a copy of the ArrayList The method .Clone creates an independent copy of the ArrayList.Changes in the copy won't affect the original ArrayList. Since the copy is also an ArrayList you will need the instruction 'Set' to assign the copy to a variable. With CreateObject("System.Collections.ArrayList")
For Each it In Array("aa1", "aa2", "aa3", "aa3", "aa2", "aa6")
End With.Add it
Nextset c_00=.Clone msgbox join(c_00.ToArray,vbLf) c_00.item(2)="~" msgbox join(.ToArray,vbLf) & vbLf & vbLf & join(c_00.ToArray,vbLf) |