Excel VBA - Issues with For Each Loop with an Array - arrays

Hoping I can find some help with this one. I am pulling in information from Excel from 2 columns (Hostname, IP). The declarations of importance are
Dim HWSWArray() As Variant
Dim v As Variant.
My HWSWArray is loaded and as an example I have HWSWArray(0)(0) -> Hostname and HWSWArray(0)(1) -> IP
I want to loop through these and do an external comparison to another string so I have my loop written as such
For Each v In HWSWArray
If v(0) = POAMHost Then
DoThings
ElseIf v(1) = POAMHost Then
Do Things
End If
Next
I am getting a type mismatch as soon as it evaluates the first if statement. I am confused because they are both variants v and HWSWArray and shouldn't v as a variant match whatever type it is matching against?
Maybe I am missing the forest for the trees? I can post the full code if necessary but will need to redact some stuff.
To add some more information with code that I can easily share, I have done something similar in the past that has worked and I cant tell what the difference is. I will look at implementing your suggestions for the For i loop if I can't get this to work but I am trying to see why it wont work in its current capacity.
'Load BArray
Do While Counter <> B1930Rows + 1
IPCell = Assets.Range("X" & Counter)
hostCell = Assets.Range("W" & Counter)
If IsEmpty(Assets.Range("W" & Counter)) = True And IsEmpty(Assets.Range("X" & Counter)) = True Then
Counter = Counter + 1
ElseIf IsEmpty(Assets.Range("W" & Counter)) = True And IsEmpty(Assets.Range("X" & Counter)) = False Then
BArray(i) = Array("Null", UCase(IPCell))
i = i + 1
Counter = Counter + 1
ElseIf IsEmpty(Assets.Range("W" & Counter)) = False And IsEmpty(Assets.Range("X" & Counter)) = True Then
BArray(i) = Array(UCase(hostCell), "Null")
i = i + 1
Counter = Counter + 1
ElseIf IsEmpty(Assets.Range("W" & Counter)) = False And IsEmpty(Assets.Range("X" & Counter)) = False Then
BArray(i) = Array(UCase(IPCell), UCase(hostCell))
i = i + 1
Counter = Counter + 1
Else
Counter = Counter + 1
End If
Loop
'MsgBox ("Here")
'Setting up script to handle the POAM Analysis Portion
Counter = 2
Dim statusCell As String
'Dim hostCell As String
Do While Counter <> POAMRows + 1
statusCell = POAM.Range("M" & Counter)
hostCell = POAM.Range("AE" & Counter)
If statusCell = "Ongoing" Then
For Each v In BArray
If v(0) = hostCell Then
Output.Range("A" & SummaryCounter) = POAM.Range("A" & Counter)
Output.Range("B" & SummaryCounter) = POAM.Range("C" & Counter)
Output.Range("C" & SummaryCounter) = POAM.Range("D" & Counter)
Output.Range("D" & SummaryCounter) = POAM.Range("E" & Counter)
Output.Range("E" & SummaryCounter) = POAM.Range("F" & Counter)
Output.Range("F" & SummaryCounter) = POAM.Range("G" & Counter)
Output.Range("G" & SummaryCounter) = POAM.Range("R" & Counter)
Output.Range("H" & SummaryCounter) = POAM.Range("V" & Counter)
Output.Range("I" & SummaryCounter) = POAM.Range("AB" & Counter)
Output.Range("J" & SummaryCounter) = POAM.Range("AE" & Counter)
SummaryCounter = SummaryCounter + 1
ElseIf v(1) = hostCell Then
Output.Range("A" & SummaryCounter) = POAM.Range("A" & Counter)
Output.Range("B" & SummaryCounter) = POAM.Range("C" & Counter)
Output.Range("C" & SummaryCounter) = POAM.Range("D" & Counter)
Output.Range("D" & SummaryCounter) = POAM.Range("E" & Counter)
Output.Range("E" & SummaryCounter) = POAM.Range("F" & Counter)
Output.Range("F" & SummaryCounter) = POAM.Range("G" & Counter)
Output.Range("G" & SummaryCounter) = POAM.Range("R" & Counter)
Output.Range("H" & SummaryCounter) = POAM.Range("V" & Counter)
Output.Range("I" & SummaryCounter) = POAM.Range("AB" & Counter)
Output.Range("J" & SummaryCounter) = POAM.Range("AE" & Counter)
SummaryCounter = SummaryCounter + 1
End If
Next
Counter = Counter + 1
Else
Counter = Counter + 1
End If
Loop

For Each and multidimensional arrays are tricky. Try this instead:
For i = LBound(HWSWArray, 1) To UBound(HWSWArray, 1)
If HWSWArray(i, 0) = POAMHost Then
'Do Things
ElseIf HWSWArray(i, 1) = POAMHost Then
'Do Things
End If
Next
LBound and UBound will give to the lower and upper bound for a given dimension, in this case the first.

Assuming you have equal length columns:
Dim HWSWArray() As Variant
HWSWArray = Range(Cells(startrow, startcolumn), Cells(endrow, endcolumn)).value
Dim i As Long
For i = lbound(HWSWArray) to ubound(HWSWArray)
If HWSWArray(v,1) = POAMHost Then
'DoThings
ElseIf HWSWArray(v,2) = POAMHost Then
'Do other Things
End If
Next i
the lowerbound (typically 0) and upper bound of the HWSWArray will be the row counts for the specific column.

To anyone looking at this in the future, I had incorrectly specified the dimensions of my array. I had extended my array to hold 3521 values but when it was loading the array, it only loaded to 3520.
When my code was running, it was comparing a variable to an empty array so I was getting a type mismatch.
I fixed the dimensions and everything worked fine.

Related

Slow copy paste in Excel vba

I have the below code and find that copy-pasting is slow and the interior colour is slow as well.
I am trying to deal with this code with 700,000 rows + 120 columns of data.
Any suggestion to improve the speed.
Currently, it can take me more than 20 mins to finish this row of code.
Application.ScreenUpdating = False
Application.DisplayAlerts = False
Application.Calculation = xlCalculationManual
Application.EnableEvents = False
Application.DisplayStatusBar = False
For i = keycolumns + 2 To ILcol + 1
'Result.Cells(1, resultcolumn).EntireColumn.Insert
rColumnLetter = Split(Cells(1, resultcolumn - 1).Address, "$")(1)
iColumnLetter = Split(Cells(1, i - 1).Address, "$")(1)
IL.Range(iColumnLetter & "1:" & iColumnLetter & ILrow).Copy Result.Range(rColumnLetter & "1:" & rColumnLetter & ILrow)
colNum = Application.WorksheetFunction.Match(Result.Cells(1, resultcolumn - 1).Value, PL.Range("1:1"), 0)
Result.Cells(1, resultcolumn) = Result.Cells(1, resultcolumn - 1) & " Postload - " & colNum
'Result.Cells(1, resultcolumn + 1).EntireColumn.Insert
Result.Cells(1, resultcolumn + 1) = Result.Cells(1, resultcolumn - 1) & " Comparison"
ColumnLetter = Split(Cells(1, resultcolumn + 1).Address, "$")(1)
Result.Range(ColumnLetter & "1:" & ColumnLetter & ILrow).Interior.Color = RGB(146, 208, 80)
resultcolumn = resultcolumn + (2 * (i - i + 1)) + 1
Next i
In my experience it is better to avoid operations directly on the sheets. What I would do is:
create an array variable
resize the array so it can hold all the data
populate the array with the actions currently included in your 'for' loop
print the array into the sheet
The final result would be close to this:
public sub populateArray()
dim arr_data() as Variant
dim numberOfRows,numberOfColumns,currentRow,currentCollumn as integer
currentRow = 0
currentCollumn = 0
numberOfRows = 10
numberOfColumns = 10
redim arr_data(numberOfRows,numberOfColumns)
for currentRow to numberOfRows
for currentCollumn to numberOfColumns
arr_data (currentRow,currentCollumn) = "TEXT"
next currentCollumn
next currentRow
with activesheet
.range("A1") = arr_data
next with
end sub
Please note that I did not test the above code, feel free to adjust it to your needs.

why does VBA exit my for loop early and why aren't cell values being stored in my array properly?

I wrote this macro to pull data from a separate workbook to do some string manipulation. (Not shown) Eventually the code uses the data stored in the arrays created to create work instructions. Code shown below with one section working as intended (bend array creation), the second section will not (csk array creation). When I step through the code it will not go to the 'next i' and exits the if statement entirely on the first iteration.
nbends = Application.WorksheetFunction.CountIf(Range("M6:M" & lastrow), "BEND RADIUS")
ncsksf = Application.WorksheetFunction.CountIf(Range("M6:M" & lastrow), "CSK FARSIDE")
ncsksn = Application.WorksheetFunction.CountIf(Range("M6:M" & lastrow), "CSK NEARSIDE")
ReDim Bends(1 To nbends, 1 To 5)
ReDim Csksn(1 To nscksn, 1 To 3)
'' Bend array creation
On Error Resume Next
Set r = Range("M6:M" & lastrow).Find(what:="BEND RADIUS", LookIn:=xlValues)
If Not r Is Nothing Then
firstaddress = r.Address
addressrow = Right(firstaddress, 2)
Bends(1, 1) = Range("A" & addressrow)
Bends(1, 2) = Range("C" & addressrow)
Bends(1, 3) = Range("A" & addressrow + 1)
Bends(1, 4) = Range("M" & addressrow + 1)
Bends(1, 5) = Range("B" & addressrow + 1)
Do
For i = LBound(Bends) + 1 To UBound(Bends)
Set r = Range("M" & addressrow & ":M" & lastrow).Find(what:="BEND RADIUS", LookIn:=xlValues)
firstaddress = r.Address
addressrow = Right(firstaddress, 2)
Bends(i, 1) = Range("A" & addressrow)
Bends(i, 2) = Range("C" & addressrow)
Bends(i, 3) = Range("A" & addressrow + 1)
Bends(i, 4) = Range("M" & addressrow + 1)
Bends(i, 5) = Range("B" & addressrow + 1)
Next i
Loop While Not r Is Nothing And r.Address <> firstaddress
End If
'' Csks array creation
On Error Resume Next
Set r = Range("M6:M" & lastrow).Find(what:="CSK NEARSIDE", LookIn:=xlValues)
If Not r Is Nothing Then
firstaddress = r.Address
addressrow = Right(firstaddress, 2)
Csksn(1, 1) = Range("A" & addressrow)
Csksn(1, 2) = Range("B" & addressrow)
Csksn(1, 3) = Range("M" & addressrow)
Debug.Print addressrow
Do
For i = LBound(Csksn) + 1 To UBound(Csksn)
Set r = Range("M" & addressrow & ":M" & lastrow).Find(what:="CSK NEARSIDE", LookIn:=xlValues)
firstaddress = r.Address
addressrow = Right(firstaddress, 2)
Csksn(i, 1) = Range("A" & addressrow)
Csksn(i, 2) = Range("B" & addressrow)
Csksn(i, 3) = Range("M" & addressrow)
Debug.Print r
Debug.Print firstaddress
Debug.Print addressrow
Next i
Loop While Not r Is Nothing And r.Address <> firstaddress
End If
I fixed this by changing the for loop parameters to
For i = 2 To ncsksn
Set r = Range("M" & addressrow & ":M" & lastrow).Find(what:="CSK NEARSIDE", LookIn:=xlValues)
firstaddress = r.Address
addressrow = Right(firstaddress, 2)
Csksn(i, 1) = Range("A" & addressrow)
Csksn(i, 2) = Range("B" & addressrow)
Csksn(i, 3) = Range("M" & addressrow)
Debug.Print r
Debug.Print firstaddress
Debug.Print addressrow
Next i
Loop While Not r Is Nothing And r.Address <> firstaddress
End If
and it will print the values for each variable (r, firstaddress, addressrow) at each iteration of the for loop; but when i go to print the array using this loop nothing appears.
For i = LBound(Csksn) To UBound(Csksn)
For j = LBound(Csksn) To UBound(Csksn)
Debug.Print i, j, Csksn(i, j)
Next j
Next i
What am I missing here?
Thank you all for your knowledge

How to use an array variable instead of a range in a formula in VBA

So, I want to use 1D and 2D arrays in place of the ranges in formulas, except that whatever I have tried has not worked. If anyone can, can they please help?
Also, when transposing my data, I don't understand how to do this by referencing other sheets.
Sub Testrun()
Cells(5, 6).Value = "=Sum([myArr])"
Set mcco = Workbooks("Book1.xlsb").Worksheets("Sheet1")
Set mcfc = Workbooks("Book1.xlsb").Worksheets("Sheet2")
Set mcfb = Workbooks("Book1.xlsb").Worksheets("Sheet3")
TR = Application.CountA(Range("A:A"))
FTNRowStart = 1
MainRowStart = 2
CVTRRowEnd = mcco.Range("A2", mcco.Range("A2").End(xlDown)).Rows.Count + 1
FCRowEnd = mcfc.Range("A2", mcfc.Range("A2").End(xlDown)).Rows.Count + 1
MNCol = 2
FNCol = 4
FTNCol = 8
CVTRmyArr = Application.Transpose(Range(Cells(MainRowStart, MNCol), Cells(CVTRRowEnd, MNCol)))
FCmyArr = Application.Transpose(Range(Cells(MainRowStart, MNCol), Cells(FCRowEnd, MNCol)))
FNmyArr = Application.Transpose(Range(Cells(MainRowStart, FNCol), Cells(FCRowEnd, FNCol)))
mcfc.Activate
For i = 2 To TR
s = 0
TCJ = Cells(1, Columns.Count).End(xlToLeft).Column + 7
For j = 8 To TCJ
TCK = mcfb.Cells(i, Columns.Count).End(xlToLeft).Column + 1
For k = 2 To TCK
XD = "=COUNTIFS(CVTRmyArr,'Sheet3'!R" & i & "C" & k & ",'Sheet3'!R[0]C" & k & ",FNmyArr,""*"" & SUBSTITUTE(MID(FNmyArr,FIND(""*"",SUBSTITUTE(FNmyArr,""("",""*"",LEN(FNmyArr) - LEN(SUBSTITUTE(FNmyArr,""("","""")))) +1,LEN(FNmyArr)),"")"",""""))"
mcfc.Cells(i, j).Value = XD
s = s + Cells(i, j).Value
Cells(i, j).Value = s
Next k
Next j
Next i
End Sub

Excel VBA deleting rows that have mixed values for a given index

I have the following data
Name ID Value
Alice 12C 500
Bob 14 60
Dan 15C 64
Dan 1C 25
Alice 4 556
Bob 11 455
In my data, Alice has both numerical (4) and string+numerical ID (12C) and I want to delete all Alice rows, while I want to hold on to data of names where their ID is strictly numeric (Bob 11, 14) or strictly string+numeric (Dan 15C , 1C).
First I make an array of unique Name entries:
FinalRow = 7
Name_column = 1
n = 1
Dim FID_Array() As Variant
ReDim Preserve FID_Array(1 To 1)
FID_Array(1) = Cells(2, Name_column)
For j = 3 To FinalRow
If Cells(j, Name_column).Value <> FID_Array(n) Then
ReDim Preserve FID_Array(1 To n + 1)
FID_Array(n + 1) = Cells(j, Name_column).Value
n = n + 1
End If
Next j
Then I make an Array of the row numbers that contain a particular Name
ReDim Preserve Count_FID_Array(1 To 1) As Variant
n = 1
range_FID = A2:A7
' In my actual code this is Range_FID
' range_FID = Cells(2, FolderId_column).Address & ":" & Cells(FinalRow, FolderId_column).Address
For Each itm5 In FID_Array()
Count_FID_Array(n) = Application.CountIf(" & range_FID & ", " & itm5 & ")
ReDim Preserve Count_FID_Array(1 To n + 1)
n = n + 1
Next itm5
I don't think my CountIf is working. I have tried to store the value of Count_FID_Array in another cell in a different sheet but I am getting #value!
If I got the countIf to work then I was going to sort the data by name, then double loop to check the ID variable the next "n" times to see if the last digit was "C" for all of them or to check if the ID was numeric for all of them.
Can you please point out why my countif is not working and is there a smarter way to do this?
I am using arrays of names here because in the end I want to feed the array into an autofilter and delete the rows that I don't want.
Update 1 3:45 PM Nov 21 2013: I have solved this as following:
I basically created three columns. First column was 0 or 1 depending on if the the ID was all numbers. The second column was 0 or 1 depending on if the last digit was "C" (in my real work the last two digits are "IB" ) and finally I compared the frequency of these occurences to the frequency of the Name itself. If any of those match then I give it the number 1 else 0. I use this index later to autofilter.
Now I'll try to use zx8754's shorter formula in the VBA code and I will try to address the issues regarding Countif that Joe has raised.
Sub conditionsforsubfolders()
FinalColumn = Cells(1, Columns.Count).End(xlToLeft).Column
FinalRow = Cells(Rows.Count, 1).End(xlUp).Row
ActiveWorkbook.ActiveSheet.Columns(FinalColumn + 1).Insert
ActiveWorkbook.ActiveSheet.Columns(FinalColumn + 2).Insert
ActiveWorkbook.ActiveSheet.Columns(FinalColumn + 3).Insert
Isnumber_Column = FinalColumn + 1
Is_IB_Column = FinalColumn + 2
Exceptions_Column = FinalColumn + 3
Cells(1, Isnumber_Column) = "Number"
Cells(1, Is_IB_Column) = "Letters"
Cells(1, Exceptions_Column) = "Exceptions"
For j = 1 To FinalColumn
If Cells(1, j).Value = "TradeId" Then
TradeId_column = j
ElseIf Cells(1, j).Value = "Total Notional per folder" Then
Total_Notional_Per_Folder_Column = j
ElseIf Cells(1, j).Value = "ExternalId" Then
ExternalId_Column = j
ElseIf Cells(1, j).Value = "FolderId" Then
FolderId_column = j
End If
Next j
range_FolderId_fixed = Cells(2, FolderId_column).Address & ":" & Cells(FinalRow, FolderId_column).Address
range_TradeId_fixed = Cells(2, TradeId_column).Address & ":" & Cells(FinalRow, TradeId_column).Address
range_Isnumber = Cells(2, Isnumber_Column).Address & ":" & Cells(FinalRow, Isnumber_Column).Address(RowAbsolute:=False, ColumnAbsolute:=False)
range_Isnumber_fixed = Cells(2, Isnumber_Column).Address & ":" & Cells(FinalRow, Isnumber_Column).Address
range_Is_IB = Cells(2, Is_IB_Column).Address & ":" & Cells(FinalRow, Is_IB_Column).Address(RowAbsolute:=False, ColumnAbsolute:=False)
range_Is_IB_fixed = Cells(2, Is_IB_Column).Address & ":" & Cells(FinalRow, Is_IB_Column).Address(RowAbsolute:=False, ColumnAbsolute:=False)
range_FolderId_cell = Cells(2, FolderId_column).Address(RowAbsolute:=False, ColumnAbsolute:=False)
range_TradeId_cell = Cells(2, TradeId_column).Address(RowAbsolute:=False, ColumnAbsolute:=False)
range_Exceptions = Cells(2, Exceptions_Column).Address & ":" & Cells(FinalRow, Exceptions_Column).Address(RowAbsolute:=False, ColumnAbsolute:=False)
Range(range_Isnumber).Formula = "=Isnumber(" & range_TradeId_cell & ")*1"
Range(range_Is_IB).Formula = "=(RIGHT(" & range_TradeId_cell & ",2)= ""IB"")*1"
Range(range_Exceptions).Formula = "=(SUMIF(" & range_FolderId_fixed & "," & range_FolderId_cell & "," & range_Isnumber_fixed & ")= COUNTIF(" & range_FolderId_fixed & "," & range_FolderId_cell & "))*1 +(SUMIF(" & range_FolderId_fixed & "," & range_FolderId_cell & "," & range_Is_IB_fixed & ")= COUNTIF(" & range_FolderId_fixed & "," & range_FolderId_cell & "))*1 "
Worksheets("Sheet1").UsedRange.AutoFilter Field:=7, Criteria1:="=1"
End Sub
Formula solution, no VBA:
=IF(SUMPRODUCT(--($A$2:$A$7=A2),--(ISNUMBER($B$2:$B$7)))=1,"delete","keep")
The problem with your CountIF call is that you're passing a poorly-formed string. You're literally passing "range_FID & ", " & itm5".
First, you set to properly define range_fid:
Dim range_fid As Range
Set range_fid = [A2:A7]
The call CountIF with:
count_fid_array(n) = Application.WorksheetFunction.CountIf(range_fid, itm5)
With that said, I would go about it differently:
Dim c As Range
Dim people As Collection: Set people = New Collection
Dim person As Collection
Dim code As String
For Each c In Range(Range("a2"), Range("a2").End(xlDown)) ' loop through all rows
If IsNumeric(c.Offset(0, 1)) Then ' check if the ID is numeric or not
code = "num"
Else
code = "alphanum"
End If
On Error Resume Next ' Needed in order to avoid error when person already exists in collection
Set person = New Collection
person.Add c.Value, "name"
person.Add code, "code"
people.Add person, c.Value ' will only be added if name doesn't already exist in collection
On Error GoTo 0
If people(c.Value)("code") <> code Then ' if the format (alpha/num) of the ID on the current row is different than the format of a previous row for this name....
people(c.Value).Remove ("code") ' then set the code to "diff"
people(c.Value).Add "diff", "Code"
End If
Next
For Each person In people ' just display the content; you can take appropriate action here
Debug.Print person("name") & ": " & person("code")
Next
The result is a Collection containing names and a code for each. The code will be one of:
num: all values for a name are numeric (Bob)
alphanum: all values for a name are alphanumeric (Dan)
diff: name has at least one each of numeric and alphanumeric (Alice)
Note that this could be done a little clearer with a Dictionary instead of a Collection, or with a Class, but I chose to take the most straightforward approach.

Add item to array in VBScript

How do you add an item to an existing array in VBScript?
Is there a VBScript equivalent to the push function in Javascript?
i.e.
myArray has three items, "Apples", "Oranges", and "Bananas" and I want to add "Watermelons" to the end of the array.
Arrays are not very dynamic in VBScript. You'll have to use the ReDim Preserve statement to grow the existing array so it can accommodate an extra item:
ReDim Preserve yourArray(UBound(yourArray) + 1)
yourArray(UBound(yourArray)) = "Watermelons"
For your copy and paste ease
' add item to array
Function AddItem(arr, val)
ReDim Preserve arr(UBound(arr) + 1)
arr(UBound(arr)) = val
AddItem = arr
End Function
Used like so
a = Array()
a = AddItem(a, 5)
a = AddItem(a, "foo")
There are a few ways, not including a custom COM or ActiveX object
ReDim Preserve
Dictionary object, which can have string keys and search for them
ArrayList .Net Framework Class, which has many methods including:
sort (forward, reverse, custom), insert, remove,
binarysearch, equals, toArray, and toString
With the code below, I found Redim Preserve is fastest below 54000, Dictionary is fastest from 54000 to 690000, and Array List is fastest above 690000. I tend to use ArrayList for pushing because of the sorting and array conversion.
user326639 provided FastArray, which is pretty much the fastest.
Dictionaries are useful for searching for the value and returning the index (i.e. field names), or for grouping and aggregation (histograms, group and add, group and concatenate strings, group and push sub-arrays). When grouping on keys, set CompareMode for case in/sensitivity, and check the "exists" property before "add"-ing.
Redim wouldn't save much time for one array, but it's useful for a dictionary of arrays.
'pushtest.vbs
imax = 10000
value = "Testvalue"
s = imax & " of """ & value & """"
t0 = timer 'ArrayList Method
Set o = CreateObject("System.Collections.ArrayList")
For i = 0 To imax
o.Add value
Next
s = s & "[AList " & FormatNumber(timer - t0, 3, -1) & "]"
Set o = Nothing
t0 = timer 'ReDim Preserve Method
a = array()
For i = 0 To imax
ReDim Preserve a(UBound(a) + 1)
a(UBound(a)) = value
Next
s = s & "[ReDim " & FormatNumber(timer - t0, 3, -1) & "]"
Set a = Nothing
t0 = timer 'Dictionary Method
Set o = CreateObject("Scripting.Dictionary")
For i = 0 To imax
o.Add i, value
Next
s = s & "[Dictionary " & FormatNumber(timer - t0, 3, -1) & "]"
Set o = Nothing
t0 = timer 'Standard array
Redim a(imax)
For i = 0 To imax
a(i) = value
Next
s = s & "[Array " & FormatNumber(timer - t0, 3, -1) & "]" & vbCRLF
Set a = Nothing
t0 = timer 'Fast array
a = array()
For i = 0 To imax
ub = UBound(a)
If i>ub Then ReDim Preserve a(Int((ub+10)*1.1))
a(i) = value
Next
ReDim Preserve a(i-1)
s = s & "[FastArr " & FormatNumber(timer - t0, 3, -1) & "]"
Set a = Nothing
MsgBox s
' 10000 of "Testvalue" [ArrayList 0.156][Redim 0.016][Dictionary 0.031][Array 0.016][FastArr 0.016]
' 54000 of "Testvalue" [ArrayList 0.734][Redim 0.672][Dictionary 0.203][Array 0.063][FastArr 0.109]
' 240000 of "Testvalue" [ArrayList 3.172][Redim 5.891][Dictionary 1.453][Array 0.203][FastArr 0.484]
' 690000 of "Testvalue" [ArrayList 9.078][Redim 44.785][Dictionary 8.750][Array 0.609][FastArr 1.406]
'1000000 of "Testvalue" [ArrayList 13.191][Redim 92.863][Dictionary 18.047][Array 0.859][FastArr 2.031]
Slight change to the FastArray from above:
'pushtest.vbs
imax = 10000000
value = "Testvalue"
s = imax & " of """ & value & """"
t0 = timer 'Fast array
a = array()
ub = UBound(a)
For i = 0 To imax
If i>ub Then
ReDim Preserve a(Int((ub+10)*1.1))
ub = UBound(a)
End If
a(i) = value
Next
ReDim Preserve a(i-1)
s = s & "[FastArr " & FormatNumber(timer - t0, 3, -1) & "]"
MsgBox s
There is no point in checking UBound(a) in every cycle of the for if we know exactly when it changes.
I've changed it so that it checks does UBound(a) just before the for starts and then only every time the ReDim is called
On my computer the old method took 7.52 seconds for an imax of 10 millions.
The new method took 5.29 seconds for an imax of also 10 millions, which signifies a performance increase of over 20% (for 10 millions tries, obviously this percentage has a direct relationship to the number of tries)
Based on Charles Clayton's answer, but slightly simplified...
' add item to array
Sub ArrayAdd(arr, val)
ReDim Preserve arr(UBound(arr) + 1)
arr(UBound(arr)) = val
End Sub
Used like so
a = Array()
AddItem(a, 5)
AddItem(a, "foo")
this some kind of late but anyway and it is also somewhat tricky
dim arrr
arr= array ("Apples", "Oranges", "Bananas")
dim temp_var
temp_var = join (arr , "||") ' some character which will not occur is regular strings
if len(temp_var) > 0 then
temp_var = temp_var&"||Watermelons"
end if
arr = split(temp_var , "||") ' here you got new elemet in array '
for each x in arr
response.write(x & "<br />")
next'
review and tell me if this can work
or initially you save all data in string and later split for array
Not an answer Or Why 'tricky' is bad:
>> a = Array(1)
>> a = Split(Join(a, "||") & "||2", "||")
>> WScript.Echo a(0) + a(1)
>>
12

Resources