While reviewing a very old VB6 working code I get a very strange statement.
aryValue = aryPersons(8, i)
Where aryValue and aryPersons are multidimensional array and declared as
dim aryPersons, aryValue
Anyone having any idea what is does?
I tried the same in test application but it is giving Type mismatch (Error 13)
ANSWER:
It is my bad to understand the VB code as I was expecting it will be strongly data type language. Actually at aryPersons(8, i) a two dimension array were getting stored and while fetching it gives use a 2D array data that can be easily assigned to aryValue as it is also a 2D array.
It is strange to me that in 2D array at any position you store a any kind of data even another 2D data.
It seems likely that aryStepPersonOptions has an array as its value:
Dim SomeArray(8, 8) As String
Dim aryStepPersonOptions, aryValue
Dim i As Long
SomeArray(8, 8) = "Hello"
aryStepPersonOptions = SomeArray
i = 8
aryValue = aryStepPersonOptions(8, i)
MsgBox aryValue
Of course the pseudo-hungarian ary prefix used seems to do more to add confusion than otherwise. Sadly far too much code contains cargo-culted messes like this. Lets hope nobody copy/pastes my throwaway example SomeArray too.
Related
I have hunted about quite a bit but can't find a way to get at the Hexadecimal or Binary representation of the content of a Double variable in VB6. (Are Double variables held in IEEE754 format?)
The provided Hex(x) function is no good because it integerizes its input first.
So if I want to see the exact bit pattern produced by Atn(1), Hex(Atn(1)) does NOT produce it.
I'm trying to build a mathematical function containing If clauses. I want to be able to see that the values returned on either side of these boundaries are, as closely as possible, in line.
Any suggestions?
Yes, VB6 uses standard IEEE format for Double. One way to get what you want without resorting to memcpy() tricks is to use two UDTs. The first would contain one Double, the second a static array of 8 Byte. LSet the one containing the Double into the one containing the Byte array. Then you can examine each Byte from the Double one by one.
If you need to see code let us know.
[edit]
At the module level:
Private byte_result() As Byte
Private Type double_t
dbl As Double
End Type
Private Type bytes_t
byts(1 To 8) As Byte
End Type
Then:
Function DoubleToBytes (aDouble As Double) As Byte()
Dim d As double_t
Dim b As bytes_t
d.dbl = aDouble
LSet b = d
DoubleToBytes = b.byts
End Function
To use it:
Dim Indx As Long
byte_result = DoubleToBytes(12345.6789#)
For Indx = 1 To 8
Debug.Print Hex$(byte_result(Indx)),
Next
This is air code but it should give you the idea.
I have a fairly complex look of code where I am looking through multiple control variables.
I am getting an error 'Invalid 'for' loop control variable
the line in questions is
for w(1) = 32 to 127
I am more familiar with VBA where I would have zero problem with this statement.
I'm guessing it has something to do with the fact that i will be looping through w(1),w(2),w(3) etc. in the same tree. I initialize the variable as dim x(10) but have also tried dim w() , dim w() redim w(10)
Any thoughts? its a fairly critical aspect of the script; as such I am unwilling to swap out all my w 1,2... for individual variables
Thoughts?
EDIT:
As per comments I should clarify a Few things:
Essentially there is a alpha numeric association with an ID in a system that I am working with which I was not handed down the key too. So I have a multi-dimensional array of rates that are used for multiplying out costs.
What I am doing is working backwards through invoices and matching a material with very subtle differences that have different pricings.
For simplicity sake, say theres a 2 dimensional material where AA, AB, ... A9 are all priced through several multiplication factors in what would just be a 2x2 grid. So maintaining a pivot point based on the position in string is very important. For this code you could take tier to mean how many characters in the string (aka how complex the composition of the material):
dim x(), w()
for tier = 1 to 2
for w(1) = 32 to 127
x(1)= chr(w(1))
If tier = 2 then
for w(2)= 32 to 127
X(2)=chr(w(2))
next
end if
str = ""
for y = 1 to (tier)
str = trim(str & x(y))
next
'''msgbox str 'debug
next
end if
str = ""
for y = 1 to (tier)
str = trim(str & x(y))
next
'' msgbox str ' debug
next 'tier
This is just an excerpt i pulled to get a basic idea of the structure w/o any calculations. this is in essence what is not working
The error is quite clear, you cannot use an Array as the control variable. The definition in For...Next Statement is even clearer;
Numeric variable used as a loop counter. The variable cannot be an array element or an element of a user-defined type.
This is one of the key differences between VBA and VBScript.
You won't loop through x(1),x(2)...on what you write it's going like this 32(1),33(1)....what type it's your w(1) and how you define him?
So, I've looked around and tried to solve this on my own. This isn't an absolutely crucial question currently, I just want to know if it could be done.
So let's say I've got a list with some data that looks like
Date Location
01/24/14 H-12
01/25/14 BB-44
01/30/14 G-12
01/29/14 7A-55
01/28/14 NN-15
01/24/14 GG-47
What I want is to be able to sort the data by Location, but I don't want it to be the general way, otherwise I'll end up with 7A-55, BB-44, G-12, H-12, NN-15. I want the data to be sorted so that double letters and single letters are sorted together. E.G. it should be G-12, H-12, BB-44, NN-15, 7A-55 once everything has been sorted.
I've tried creating a custom list sort, but it doesn't work. the way intended. The custom list I tried was A-Z, AA-ZZ, 7A (items were listed out, but for saving space I wrote them like that).
Like I said, this isn't a particularly huge deal if it can't be done, it just would have made it a little easier.
Edit 1 Here is what I would like to be the output
Date Location
01/30/12 G-12
01/24/14 H-12
01/25/14 BB-44
01/24/14 GG-47
01/28/14 NN-15
01/29/14 7A-55
Edit
All of these worked in the regards i wanted to, although if I had to choose a favorite it would be the base 36 number conversion one. That was some real out-of-the-box thinking and the math geek in me appreciated it. Thanks everyone!
Well it works, but is a bit complex, so rather just for fun:
This UDF returns a value that can be used as sort key. It transforms the code into a four-digit base 36-number, i.e. using A-Z and 0-9 as symbols (like hex uses 0-9 and A-F). To get at your desired output, I literally put the symbols in this order, letters first (so "A" = 0 and "0" = 26).
(The missing 'digits' are filled up with zeros, which are in this case "A"s)
It works ;)
Public Function Base36Transform(r As Range) As Long
Dim s As String, c As String
Dim v
Dim i As Integer
Dim rv As Long
v = Split(r.Text, "-")
s1 = v(0)
s2 = v(1)
s = Right("A" & s1, 2) & Right("A" & s2, 2)
rv = 0
For i = 1 To Len(s)
c = Mid(s, Len(s) - i + 1, 1)
If c Like "#" Then
rv = rv + (Val(c) + 26) * (36 ^ (i - 1))
Else
' c is like "[A-Z]"
rv = rv + (Asc(c) - Asc("A")) * (36 ^ (i - 1))
End If
Next
Base36Transform = rv
End Function
Sorting is often a very creative process. VBA can ease up the process, but a little extension of the data will work just as well.
See my results:
The way I did it is by getting the length of each string, just to be safe. This is gotten by simply going =LEN(B2), dragged down.
Then I check if it starts with 7. If it does, assign 1, otherwise keep at 0. I used this formula: =(LEFT(B2,1)="7")*1, dragged down.
Now, my custom sort is this:
Now I might have gotten some things wrong here, or I might even have done overkill by going the Length column. However, the logic is pretty much what you're aiming for.
Hope this helps in a way! Let us know. :)
I am a little lazy here and assuming your data sits in Column A,B. You mightneed to adjust your range or the starting point of your list. But here's the code:
Sub sortttttt()
Dim rng As Range
Dim i As Integer
Range("B2").Activate
Do While Not IsEmpty(ActiveCell)
ActiveCell.Value = Len(ActiveCell.Value) & ActiveCell.Value
ActiveCell.Offset(1, 0).Activate
Loop
Set rng = Range("A1:B6")
rng.Sort Key1:=Range("B2"), Order1:=xlAscending, Header:=xlYes
Range("B2").Activate
Do While Not IsEmpty(ActiveCell)
ActiveCell.Value = Right(ActiveCell.Value, Len(ActiveCell.Value) - 1)
ActiveCell.Offset(1, 0).Activate
Loop
End Sub
Assuming your data is in columns B:C with labels in Row1 and no intervening blank rows, add a column with:
=IF(ISNUMBER(VALUE(LEFT(C2))),3,IF(FIND("-",C2)>2,2,1))
in D1 copied down to suit and sort ascending Location within sort ascending of the added column.
I'm tying to make something in VBA that will basically list all the files in one or more directories starting from a root folder. Long story short, I'm using filesystemobject to run through all of the folders and then getting all the files in those folders. Moving to the next folder, etc.
The problem I'm running into is that I need to spit out my data (onto a sheet) in the same folder sort order as one might find in Windows. I know this isn't a fixed concept per say, so here's a quick example, as it's displayed in Windows(for me):
Windows Sort Order:
FolderTest\000
FolderTest\0
FolderTest\0001
Not too surprisingly, when using FSO it returns the sub folders in a different (perhaps more logical) order:
FolderTest\0
FolderTest\000
FolderTest\0001
I was hoping someone might have an idea of what one could do to get this to be resorted as it's displaying in Windows. This is just an example obviously, the files could be named anything, but it certainly seems to behave a lot better with alpha characters in the name. I'm not necessarily married to using FSO, but I don't even know where else to look for an alternative. I know I could potentially resort these in an array, but I'm not sure what kind of wizardry would be required to make it sort in the "proper" order. For all I know, there's some method or something that makes this all better. Thanks in advance for any help!
To whoever it may end up helping, the following code looks like it's giving me the results I was looking for, converting a list of subfolders into the same sort orders you (probably) find in Windows Explorer. Feeding in Subfolders from a Filesystem object, it spits the results out in an array (fnames). The code... it's not pretty. I'll be the first to admit it. Don't judge me too harshly. Big thanks #Paddy (see above) for pointing me towards StrCmpLogicalW (http://msdn.microsoft.com/en-us/library/windows/desktop/bb759947(v=vs.85).aspx)
Private Declare PtrSafe Function StrCmpLogicalW Lib "shlwapi" _
(ByVal s1 As String, ByVal s2 As String) As Integer
Sub filefoldersortWindows()
Dim folder As String
Dim fnames() As String, buffer As String, content As String
folder = "Your Path"
Set fsol = CreateObject("Scripting.fileSystemObject")
Set fold = fsol.GetFolder(folder)
FoldCount = fold.SubFolders.Count
ReDim fnames(FoldCount)
cFcount = 0
For Each fld In fold.SubFolders
cFcount = cFcount + 1
Namer$ = fld.Name
fnames(cFcount) = StrConv(Namer, vbUnicode)
Next
For AName = 1 To FoldCount
For BName = (AName + 1) To FoldCount
If StrCmpLogicalW(fnames(AName), fnames(BName)) = 1 Then
buffer = fnames(BName)
fnames(BName) = fnames(AName)
fnames(AName) = buffer
End If
Next
Next
For i = 1 To FoldCount
fnames(i) = StrConv(fnames(i), vbFromUnicode)
If i > 1 Then
content = content & "," & fnames(i)
Else
content = fnames(i)
End If
Next
End Sub
Ahh, I see now. I made a bunch of directories with numeric names to see what's going on. Windows explorer does an integer conversion on the value. The sort rule is like this:
numeric value : ascending
padding length : descending
So, if have 01 and 001, both evaluate to the integer 1, but 001 will appear first because it is longer (has more zero-padding). The 'length' in this case only refers to the numeric part (ie the padding), and is not affected by any characters that appear after (they only matter if the numeric value and the padding length are the same - then normal ordering applies):
I have declared a two dimensional array in the function library and associated it with a test. In action1 of the test, I tried to clear the array using "erase" statement.
My code -
In Function Library,
Dim strVerifyAry(25,6)
In action1,
erase strVerifyAry
Error message
Run Error - Type mismatch: 'Erase'
How to clear the contents of this array?
Works for me in plain VBScript, so it's most likely an issue with whatever engine QTP uses for running VBScript code. You should be able to emulate the behavior of Erase for a 2-dimensional array like this:
Sub EraseArray(ByRef arr)
For i = 0 To UBound(arr, 1)
For j = 0 To UBound(arr, 2)
If IsObject(arr(i, j)) Then
Set arr(i, j) = Nothing
Else
arr(i, j) = Empty
End If
Next
Next
End Sub
Or like this, if you don't want to set fields containing objects to Nothing:
Sub EraseArray(ByRef arr)
For i = 0 To UBound(arr, 1)
For j = 0 To UBound(arr, 2)
arr(i, j) = Empty
Next
Next
End Sub
I do not exactly understand why, but you can create a sub like
Public Sub DoErase (byRef Ary)
Erase Ary
End Sub
in the library, and call it from within the action like this:
DoErase StrVerifyAry
and that works.
Update: No it doesn't. The array is successfully passed to DoErase, and the DoErase call works fine, but the test afterwards still can reference the array elements that Erase was supposed to be erasing.
If the test declares the array, it works fine (Erase erases the elements).
This is very strange and probably has to do with the quirky scopes in function libraries.
Please let us know if you ever find out what's going on here...
This drove me nuts for an entire afternoon so I wanted to post an answer for future reference. I filled an array using the Split command and then needed to Erase it before the script looped back through the process again. Nothing I tried would erase or clear the array and the next use of Split just appended to the previous array elements.
By trying the 'array=Nothing' loop above, I finally managed to generate a "This array is fixed or locked" error which I researched. Turns out I had used the array in a 'For Each..Next' loop which locks the array so it can't be erased or cleared. More info is available HERE:
You can use a Dictionary collection rather than an array in some circumstances. Then use RemoveAll when you want to clear it. That doesn't help when your array was created by a split function, or whatever, but it can help in other use cases.
Set myDict = CreateObject("Scripting.Dictionary")
...
myDict.RemoveAll
Refer to: https://www.w3schools.com/asp/asp_ref_dictionary.asp