Dim or ReDim issue? - vbscript

I have a questions about classic ASP:
Can anyone tell me what I am doing wrong in this snippet? If ORDERDATA() contains one entry only, it works fine. If more than one, it crashes. I am pretty sure it has to do with the Dim/ReDim of the CARTITEMS() variable, but I can't see what I'm doing wrong.
Some of the dimmed variables are used in other parts of the script, please ignore them.
Dim i,countOrderRows, orderdata, XXX
orderdata = Order_GetOrderData()
countOrderRows = ubound(orderdata,1)
Dim cartItems()
ReDim cartItems(countOrderRows-1)
Dim goodsList(), addr, klarnaresponse, resp, sql, item
ReDim goodsList(countOrderRows-1)
for i = 0 to countOrderRows - 1
Set item = Server.CreateObject("Scripting.Dictionary")
item.Add "reference", "XX1"
item.Add "name", orderdata(i,2)
item.Add "quantity", cint(orderdata(i,5))
item.Add "unit_price", (cint(orderdata(i,3)*100))
item.Add "discount_rate", 0
item.Add "tax_rate", 2500
Set cartItems(i) = item
Set item = nothing
next
Hope you guys can help!
Regards, Bob

countOrderRows = ubound(orderdata,1)
assigns the maximum 0-based index to countOrderRow, not the number of elements.
Later, you use countOrderRows as if it contained the number of elements:
ReDim cartItems(countOrderRows-1)
For an orderdata with only one element in dimension 1, this will do ReDim (..,-1), which is not what you intended, but hey!
Well, why this doesn't yield an error is a separate question ;)
And then, you use
for i = 0 to countOrderRows-1
to iterate. That´s wrong. UBound returns the maximum acceptable index. For one element, this is 0. Since countOrderRows equals 0, you are trying a
for i = 0 to -1
which again is not what you intended.
And that´s why things work (seem to work) for only one element: A for loop from 0 to -1 is executed zero times. If there is more than one element, the loop code is executed, and fails due to the wrong indexes and array sizes involved.
Note I intentionally did not just spit out corrected code, but tried to lead you into fixing this bug yourself.

Assuming that Order_GetOrderData() is a multidimensional array in the format
function Order_GetOrderData ()
dim myArray(0,5)
myArray(0, 0) = "XX1"
myArray(0, 1) = "G66T"
myArray(0, 2) = "An Item"
myArray(0, 3) = "3"
myArray(0, 4) = "0"
myArray(0, 5) = "100"
Order_GetOrderData = myArray
end function
Removing the -1 from countOrderRows in all three places it's used allowed me to return the item values for each iteration.
Dim i,countOrderRows, orderdata, XXX
orderdata = Order_GetOrderData()
countOrderRows = ubound(orderdata, 1)
Dim cartItems()
ReDim cartItems(countOrderRows)
Dim goodsList(), addr, klarnaresponse, resp, sql, item
ReDim goodsList(countOrderRows)
for i = 0 to countOrderRows
Set item = Server.CreateObject("Scripting.Dictionary")
item.Add "reference", "XX1"
item.Add "name", orderdata(i,2)
item.Add "quantity", cint(orderdata(i,5))
item.Add "unit_price", (cint(orderdata(i,3)*100))
item.Add "discount_rate", 0
item.Add "tax_rate", 2500
Set cartItems(i) = item
Set item = nothing
next
response.write cartItems(0).item("name")
response.write cartItems(1).item("name")
etc.
The reason for this being that ubound(orderdata, 1) returns the number of rows in a ZERO based form so subtracting 1 from this gave a negative value when only one result existed.
I am unsure why your code works when you pass it one item and not multiple items. The example provided using my assumed data works with multiple items as long as you are looking to return from the beginning of the data set, but will always fail if only one item is returned. It may have something to do with the format of the array returned by Order_GetOrderData()?

Related

Iterate over all selected rows of SSDBGrid to fill an array

I need to iterate over all of the selected rows in an SSDBGrid. Then, I need to get the value in the current row and populate the relevant place in the array with this value.
I've been attempting to do this with the code below:
Dim i As Integer
i = 0
Dim nomCode(Grd_Nominal.SelBookmarks.Count) As String ' This is my array.
Do While Grd_Nominal.SelBookmarks <> 0
nomCode(i) = Grd_Nominal.SelBookmarks(0)
If Grd_Nominal.SelBookmarks.Count > 0 Then
Grd_Nominal.SelBoomarks(0).Remove
End If
i = i + 1
Loop
However, nomCode(i) is always being filled as nomCode(i) = "??"
Why is it inserting "??", and how can I fix this to insert the value of the current row?
You need to first of all re-think how you're declaring your array.
Dim nomCode() As String
ReDim nomCode(Grd_Nominal.SelBookmarks.Count - 1) As String
This is because when declaring an array you need to pass in a constant as the length. ReDim doesn't, so this will go partly towards solving the issue.
Dim x As Integer
Dim bk As Variant
For x = 0 to Grd_Nominal.Rows.Count - 1
bm = Grd_Nominal.SelBookmarks(x)
nomCode(x) = Grd_Nominal.Columns("Your_Column").CellValue(bm)
Next
This should sort the rest of the issue, I think.

Visual Basic: Filter Items from a listbox

I'm trying something seemingly simple but I've been cracking my head over this problem for days now.
All I want to do is when the button 'Filter' is pressed, that all items that contain the string "Item" are removed from the listbox.
This is my current code:
Dim index As Integer = 0
Dim amountItems As Integer = LSBItems.Items.Count - 1
For i As Integer = 0 To amountItems
LSBItems.SelectedIndex = index
Dim l_text As String = LSBItems.SelectedItem.ToString
If l_text.Contains("Item") Then
LSBItems.Items.Remove(LSBItems.SelectedItem)
End If
index = index + 1
Next
Screenshot:
Just in case someone has a similar problem, I was able to solve this issue using a reversed loop:
For i As Integer = LSBItems.Items.Count - 1 To 0 Step -1
If LSBItems.Items(i).Contains("Item") Then
LSBItems.Items.RemoveAt(i)
End If
Next

Outlook VBA Code extremely slow

I wrote this code that seeks for a particular string in all messages within all mailboxes (average of 100 mesages per mailbox (10 mailboxes total)).
The thing is... the code works but it is too damn slow, even freezes Outlook.
Is there something I can do to make it faster?
Sub InboxSeeker(Word As String)
Dim u As Integer, AddressArr() As String, Users() As String, Element As Variant, Label As Control
GetOutlook
AddressArr = QryLoop_Specific("Company", "Address", "Users", "Team", "Samples", "Address")
For Each Element In AddressArr
Set lFolder = GetFolder(Element)
Set lItems = GetFolder(Element).Items
For Each lMsg In lItems
If InStr(1, lMsg.Body, Word, vbTextCompare) > 0 Or InStr(1, lMsg.Subject, Word, vbTextCompare) > 0 Then
DoEvents
ReDim Preserve Users(u)
Users(u) = QrySingleResult("Company", "FullName", "Users", "Address", Element)
u = u + 1
End If
Next lMsg
Next Element
I am not fully sure why you need DoEvents at each iterations, but you might need it in your GUI, otherwise just do it once at the end.
I believe that ReDim of the array all the time is not very efficient. Why not using a Collection?
Collections vs Array
You could change your code to include
Dim Users as new Collection
...
Users.Add QrySingleResult("Company", "FullName", "Users", "Address", Element)
For Each Element In AddressArr
Set lFolder = GetFolder(Element)
Set lItems = GetFolder(Element).Items
For Each lMsg In lItems
Instead of iterating over all folders and items in Outlook you need to use the Find/FindNext or Restrict methods of the Items class to find Outlook items that matches your conditions.
Also I'd suggest using the AdvancedSearch method of the Namespace class which performs a search based on a specified DAV Searching and Locating (DASL) search string.
Use Items.Find/FindNext
set item = lItems.Find("#SQL=(""urn:schemas:httpmail:textdescription"" LIKE '%something%') OR (""http://schemas.microsoft.com/mapi/proptag/0x0E1D001F"" LIKE '%something%') ")
while Not (item is Nothong)
...
set Item = lItems.FindNext
wend

Excel VBA: Writing an array to cells is very slow

I am working with VBA in Excel to retrieve some information from the Reuters 3000 Database. The data I retrieve comes as a bidimensional array consisting of one column holding dates and other column holding numeric values.
After I retrieve the information, a process that takes no more than 2 seconds, I want to write this data to a worksheet. In the worksheet I have a column with dates and several other columns with numeric values, each column containing values of a same category. I iterate over the rows of the array to get the date and numeric value and I keep those in a variable, then I search for the date on the date column of the worksheet and after I've found the date I write the value. Here is my code:
Private Sub writeRetrievedData(retrievedData As Variant, dateColumnRange As String, columnOffset As Integer)
Dim element As Long: Dim startElement As Long: Dim endElement As Long
Dim instrumentDate As Variant: Dim instrumentValue As Variant
Dim c As Variant: Dim dateCellAddress As Variant
Application.ScreenUpdating = False
Sheets("Data").Activate
startElement = LBound(retrievedData, 1): endElement = UBound(retrievedData, 1)
Application.DisplayStatusBar = True
Application.StatusBar = "Busy writing data to worksheet"
For element = startElement To endElement
instrumentDate = retrievedData(element, 1): instrumentValue = retrievedData(element, 2)
Range(dateColumnRange).Select
Set c = Selection.Find(What:=instrumentDate, After:=ActiveCell, LookIn:=xlValues, _
LookAt:=xlWhole, SearchOrder:=xlByColumns, SearchDirection:=xlNext, _
MatchCase:=False, SearchFormat:=False)
If Not c Is Nothing Then
c.offset(0, columnOffset).Value = instrumentValue
End If
Next element
Application.DisplayStatusBar = False
End Sub
My problem is that this process is very slow, even if I have only 5 rows in the array it takes about 15 seconds to complete the task. As I want to repeat this process several times (once per each set of data I retrieve from the database), I would like to decrease the execution time as much as possible.
As you can see, I am disabling the update of the screen, which is one of the most recurrent actions to improve performance. Does anybody have a suggestion on how I can further decrease the execution time?
PS. I know the data retrieval process does not take much because I already tested that part (displaying values on a MsgBox as soon as the data has been retrieved)
Thanks in advanced.
This is what I did to improve the performance:
Avoid selecting the cell when the value is going to be written. This was a suggestion of Tim Williams.
I set the property Application.Calculation to xlCalculationManual
Instead of using the Find() function to search for the date, I loaded all the dates from the worksheet into an array and iterate over this array to get the row number. This turns out to be faster than the Find() function.
Private Function loadDateArray() As Variant
Dim Date_Arr() As Variant
Sheets("Data").Activate
Date_Arr = Range(Cells(3, 106), Cells(3, 106).End(xlDown))
loadDateArray = Date_Arr
End Function
Private Function getDateRow(dateArray As Variant, dateToLook As Variant)
Dim i As Double: Dim dateRow As Double
For i = LBound(dateArray, 1) To UBound(dateArray, 1)
If dateArray(i, 1) = dateToLook Then
dateRow = i
Exit For
End If
Next i
getDateRow = dateRow
End Function
Thank you all for your help!
By not selecting the sheet, you can add a bit more speed. Instead of
Sheets("Data").Activate
Date_Arr = Range(Cells(3, 106), Cells(3, 106).End(xlDown))
loadDateArray = Date_Arr
Try
With Sheets("Data")
Date_Arr = .Range(Cells(3, 106), Cells(3, 106).End(xlDown))
End With

Count what number that appears the most. In VBScript

I got this comma separated file with a bunch of numbers
The only thing that I need to be able to do is to find what number that appears the most time
Ex:
817;9;516;11;817;408;9;817
then the result will be 817
I hope you understand what I am trying to do.
I would suggest using the FileSystemObjects, specifically the OpenTextFile method to read the file, then use split function to separate based on columns. Then iterate the array returned, and count the number of times each number occurs.
The following code will count your array for you. It uses the useful Dictionary object.
Set counts = CreateObject("Scripting.Dictionary")
For i = Lbound(arr) to Ubound(arr)
If Not counts.Exists(arr(i)) Then
counts.add arr(i), 1
Else
currCount = counts.Item(arr(i))
counts.Item(arr(i)) = currCount + 1
End If
Next
nums = counts.Keys()
currMax = 0
currNum = 0
For i = Lbound(nums) to Ubound(nums)
If counts.Item(nums(i)) > currMax Then
currMax = counts.Item(nums(i))
currNum = nums(i)
End If
Next
num = currNum ' Most often found number
max = currMax ' Number of times it was found
i would go through the text and count the number of your nubmers.
after that i would redim an dynamic array.
- go throught the text from beginning to end, and store them in the array.
after that i would pick the first number, go through the array and count (for example in tmpcounter) the number of dublicates. [you could store the counted number from the textfile in tmphit]
the you pick the second number, count the number of dublicates ( tmpcounter2 /tmphit2)
compare the two counters,you "keep" the higher one and use the lowe one for the next number
...go on until the last field is validated.
at the end you know which number appearse most and how often.
i hope this help you.
this is how i would programm it, maybe there is a better way or an API.
at the end you know
Try this
Set objFile = CreateObject("Scripting.FileSystemObject").OpenTextFile("C:\Test.txt",1)
Set dictNumbers = CreateObject("Scripting.Dictionary")
Dim MostKey
intHighest = -1
do while NOT objFile.AtEndOfStream
LineArray = Split(objFile.ReadLine,";")
for i = 0 to UBound(LineArray)
if dictNumbers.Exists(LineArray(i)) Then
dictNumbers.Item(LineArray(i)) = dictNumbers.Item(LineArray(i)) + 1
else
dictNumbers.Add LineArray(i), 1
end if
if dictNumbers.Item(LineArray(i)) > intHighest Then
intHeighest = dictNumbers.Item(LineArray(i))
MostKey = LineArray(i)
end if
next
Loop
MsgBox MostKey

Resources