Populating VBscript array with modern array - vbscript

Arrays tend to look like this in languages such as JavaScript and Python:
['Foo', 'Bar']
But in classic ASP they look like this:
Array("foo", "bar")
Is there an easy way to store a modern array in a VBScript array?
As an example of the problem this will give a type mismatch:
Dim arrayString
arrayString = "['Foo', 'Bar']"
Dim myArray()
myArray = array(arrayString)
If someone has built a library to do this conversion that would be most helpful, otherwise I'm guessing we need to do something cumbersome with split?

Are you aware that ASP classic also supports JScript (ECMAScript Version 3)? For instance, we can put function wrappers on JSON parsing and stringify functions and JScript arrays as follows:
<%# Language= "Javascript" %>
<%
//json2-min.js
if(typeof JSON!=="object"){JSON={}}(function(){"use strict";function f(e){return e<10?"0"+e:e}function quote(e){escapable.lastIndex=0;return escapable.test(e)?'"'+e.replace(escapable,function(e){var t=meta[e];return typeof t==="string"?t:"\\u"+("0000"+e.charCodeAt(0).toString(16)).slice(-4)})+'"':'"'+e+'"'}function str(e,t){var n,r,i,s,o=gap,u,a=t[e];if(a&&typeof a==="object"&&typeof a.toJSON==="function"){a=a.toJSON(e)}if(typeof rep==="function"){a=rep.call(t,e,a)}switch(typeof a){case"string":return quote(a);case"number":return isFinite(a)?String(a):"null";case"boolean":case"null":return String(a);case"object":if(!a){return"null"}gap+=indent;u=[];if(Object.prototype.toString.apply(a)==="[object Array]"){s=a.length;for(n=0;n<s;n+=1){u[n]=str(n,a)||"null"}i=u.length===0?"[]":gap?"[\n"+gap+u.join(",\n"+gap)+"\n"+o+"]":"["+u.join(",")+"]";gap=o;return i}if(rep&&typeof rep==="object"){s=rep.length;for(n=0;n<s;n+=1){if(typeof rep[n]==="string"){r=rep[n];i=str(r,a);if(i){u.push(quote(r)+(gap?": ":":")+i)}}}}else{for(r in a){if(Object.prototype.hasOwnProperty.call(a,r)){i=str(r,a);if(i){u.push(quote(r)+(gap?": ":":")+i)}}}}i=u.length===0?"{}":gap?"{\n"+gap+u.join(",\n"+gap)+"\n"+o+"}":"{"+u.join(",")+"}";gap=o;return i}}if(typeof Date.prototype.toJSON!=="function"){Date.prototype.toJSON=function(){return isFinite(this.valueOf())?this.getUTCFullYear()+"-"+f(this.getUTCMonth()+1)+"-"+f(this.getUTCDate())+"T"+f(this.getUTCHours())+":"+f(this.getUTCMinutes())+":"+f(this.getUTCSeconds())+"Z":null};String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(){return this.valueOf()}}var cx,escapable,gap,indent,meta,rep;if(typeof JSON.stringify!=="function"){escapable=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;meta={"\b":"\\b"," ":"\\t","\n":"\\n","\f":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"};JSON.stringify=function(e,t,n){var r;gap="";indent="";if(typeof n==="number"){for(r=0;r<n;r+=1){indent+=" "}}else if(typeof n==="string"){indent=n}rep=t;if(t&&typeof t!=="function"&&(typeof t!=="object"||typeof t.length!=="number")){throw new Error("JSON.stringify")}return str("",{"":e})}}if(typeof JSON.parse!=="function"){cx=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;JSON.parse=function(text,reviver){function walk(e,t){var n,r,i=e[t];if(i&&typeof i==="object"){for(n in i){if(Object.prototype.hasOwnProperty.call(i,n)){r=walk(i,n);if(r!==undefined){i[n]=r}else{delete i[n]}}}}return reviver.call(e,t,i)}var j;text=String(text);cx.lastIndex=0;if(cx.test(text)){text=text.replace(cx,function(e){return"\\u"+("0000"+e.charCodeAt(0).toString(16)).slice(-4)})}if(/^[\],:{}\s]*$/.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"#").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]").replace(/(?:^|:|,)(?:\s*\[)+/g,""))){j=eval("("+text+")");return typeof reviver==="function"?walk({"":j},""):j}throw new SyntaxError("JSON.parse")}}})()
function jsonParse(str) { return JSON.parse(str); }
function jsonStringify(obj) { return JSON.stringify(obj); }
function jsArray() { return []; }
function jsArrayPush(a,v) { a.push(v); }
%>
Then, in your VBScript code you can make use of the JScript array, e.g.
<%# Language= "VBscript" %>
<%
Dim arr, str
str = "[""Foo"", ""Bar""]"
Set arr = jsonParse(str)
Call jsArrayPush(arr, "Hello")
Call jsArrayPush(arr, "World")
str = jsonStringify(arr) ' ["Foo","Bar","Hello","World"]
%>

You're comparing two different things. Your example of a "modern array" is what is called literal syntax. This tells the processor to create an array using the data and structure provided. The indexing order is typically preserved in these languages.
Your classic examples using Array() are array generating functions. They create arrays in memory with provided values, but don't necessarily imply any sort of structure at all. Many languages specifically indicate that value indexing can vary. Some languages process arrays internally as objects and often require the new keyword in addition to the array function. When this happens it's often referred to as constructor syntax.
Many languages offer one, two, or even all three ways of creating arrays.
VBScript does not offer any variation of the literal syntax.

Related

Using one variable for multiple items data in descriptive programming

I know that with Descriptive programming you can do something like this:
Browser("StackOverflow").Page("StackOverflow").Link("text:=Go To Next Page ", "html tag:=A").Click
But is it possible to create some kind of string so I can assign more than one data value and pass it as single variable? I've tried many combinations using escape characters and I always get error.
For example in the case above, let's say I have more properties in the Page object, so I'd normally have to do something like this:
Browser("StackOverflow").Page("name:=StackOverflow", "html id:=PageID")...etc...
But I'd like to pass "name:=StackOverflow", "html id:=PageID" as a single variable, so when writing many objects I'd only have to write:
Browser(BrowserString).Page(PageString).WebEdit("name:=asdfgh")
And the first part would remain static, so if the parents' data needs to be modified I'd only have to modify two variables and not all the objects created in all libraries.
Is it possible?
If I was not clear enough please let me know.
Thank you in advance!
I think what you're looking for is UFT's Description object
This allows you finer grained control on the description since in descriptive programming all values are regular expressions but with Description you can turn the regular expression functionality off for a specific property.
Set desc = Description.Create()
desc("html tag").Value = "A"
desc("innertext").Value = "More information..."
desc("innertext").RegularExpression = False
Browser("Example Domain").Navigate "www.example.com"
Browser("Example Domain").Page("Example Domain").WebElement(desc).Click
If you want to represent this with plain string then it's a bit more of a problem, you can write a helper function but I'm not sure I would recommend it.
Function Desc(descString)
Set ret = Description.Create()
values = Split(descString, "::")
For Each value In values
keyVal = Split(value, ":=")
ret(keyVal(0)).Value = keyVal(1)
Next
Set Desc = ret
End Function
' Usage
Browser("StackOverflow").Page("StackOverflow").WebElement(Desc("html tag:=H2::innertext:=some text")).Click
Further reading about descriptive programming.
As an alternative to Motti's excellent answer, you could also Set a variable to match your initial descriptive object and then extend it as required:
Set myPage = Browser("StackOverflow").Page("name:=StackOverflow", "html id:=PageID")
after which you can then use
myPage.WebEdit("name:=asdfgh")
throughout the rest of the code, so long as the myPage object stays in scope...

If Not IsNull in ASP Classic

I'm fairly new to asp and I've got a syntax error I would like help on if you can.
I've got an ASP page that shows a table that pulls data from sql. Most of the data hasn't been populated yet so returns a NULL. The data type in question is numeric. I need to FormatNumber the rs when it is not null and not populate if it is.
This is what I have
<%=If Not IsNull(rs("ContractValue")) Then FormatNumber(rs("ContractValue"),0) end if%>
But as mentioned, im getting a syntax error.
What am i doing wrong?
I would recommend not using IsNull() in this scenario, but to answer the question about the syntax error first.
The reason is the <%= %> syntax which is shorthand for
<% Response.Write %>
in Classic ASP.
So what you are actually doing if written without the shorthand approach is;
<% Response.Write If Not IsNull(rs("ContractValue")) Then FormatNumber(rs("ContractValue"),0) End If %>
which is incorrect syntax and will trigger a Syntax Error.
To fix the code remove the = from the <% %> tags, like so;
<% If Not IsNull(rs("ContractValue")) Then Response.Write FormatNumber(rs("ContractValue"),0) End If %>
What about using IsNull?
While this can work it can often give weird results because a DBNull (depending on the database being used) can be different and is often different to the VBScript vbNull variant.
Because of this and the fact VBScript isn't strongly typed I find it useful to use a simple quick cast to string to avoid Nulls then check for valid data.
Example numeric check
Dim contractValue
contractValue = rs("ContractValue") & ""
If Len(contractValue) > 0 And IsNumeric(contractValue) Then contractValue = Clng(contractValue) Else contractValue = 0
You can take this further by writing a reusable piece of code that IIf() function explained in this post.
How to do a single line If statement in VBScript for Classic-ASP? (Mentioned by #TasosK in the comments)
Something like this;
Dim contractValue
contractValue = rs("ContractValue") & ""
contractValue = IIf(Len(contractValue) > 0 And IsNumeric(contractValue), contractValue, 0)
#Paul made a good point about evaluation of parameters, in the original code would potentially break
contractValue = IIf(Len(contractValue) > 0 And IsNumeric(contractValue), Clng(contractValue), 0)
because Clng(contractValue) would be evaluated regardless of whether the outcome was True or False. So any formatting would need to be afterwards or a more complex version of the IIf() function be built.
If Not IsNull(rs("ContractValue")) Then
<%=FormatNumber(rs("ContractValue"),0)%>
end if
Do not be in a hurry with Classic ASP.
I'm sure you want to insert content in between some HTML code which made you bunch up all that code. If that is the case, I suggest you separate VBscript code from HTML like below for example;
<%
Dim valueToOutput
If Not IsNull(rs("ContractValue")) Then
valueToOutput=FormatNumber(rs("ContractValue"),0)
end if
%>
<!-- HTML Code continues below with an inserted VBscript variable -->
There are a total of <%=valueToOutput%> oranges available!
If dealing with too many null fields, the code will be riddled with too many IF-THEN-ELSE statements and that would look really ugly.
Consider using the COALESCE function on the database side, so the field values don't come up as null on the recordset, or alternatively, consider using your own coalesce function in ASP that you can use over and over again.
Function Coalesce(inputValue, replaceWith)
if isnull(X) then
Coalesce = replaceWith
else
Coalesce = inputValue
end if
End Function
Then you can use something like this:
<%=FormatNumber(Coalesce(rs("ContractValue"),0),0)%>

How do I pass an array of strings to a ruby method that wants the strings but not in an array?

I have an array of strings (actually file names), something like
filenames = ['file1.jpg', 'file2.jpg', 'file3.jpg']
The method I am calling expects something like
images = Magick::ImageList.new("image1.png", "image2.png", "image3.png")
but if I call as below, I am actually passing an array.
images = Magick::ImageList.new(filenames)
How do I unwrap the contents of the array?
Do as below using splat opearator(*) :
images = Magick::ImageList.new(*filenames)
As #Stefan mentioned the documentation link for the same Array to Arguments Conversion

VB6 Tuple Equivalent?

I'm porting some C# code to VB6 because legacy applications. I need to store a list of pairs. I don't need to do associative lookups, I just need to be able to store pairs of items.
The snippet I'm porting from looks like this:
List<KeyValuePair<string, string>> listOfPairs;
If I were to port this to C++, I'd use something like this:
std::list<std::pair<string, string> > someList;
If this were python I'd just use a list of tuples.
someList.append( ("herp", "derp") )
I'm looking for a library type, but will settle for something else if necessary. I'm trying to be LAZY and not have to write cYetAnotherTinyUtilityClass.cls to get this functionality, or fall back on the so-often-abused string manipulation.
I've tried googling around, but VB6 is not really documented well online, and a lot of what's there is, well challenged. If you've ever seen BigResource, you'll know what I mean.
Collections of Variants can be quite flexible and unless you are really beating on them performance is not an issue:
Private Sub SomeCode()
Dim Pair As Variant
Dim ListOfPairs As Collection
Set ListOfPairs = New Collection
With ListOfPairs
Pair = Array("this", "that")
.Add Pair
.Add Array("herp", "derp")
.Add Array("weet", "tweet")
MsgBox .Item(1)(0) 'Item index is base-1, array index base-0.
Pair = .Item(2)
MsgBox Pair(1)
ReDim Pair(1)
Pair(0) = "another"
Pair(1) = "way"
.Add Pair
MsgBox .Item(4)(1)
End With
End Sub
If its literally just for storage you can use a Type:
Public Type Tuple
Item1 As String
Item2 As String
End Type
Its a bit more concise than needing a class to do the storage.
The problem with Types (known more widely as UDTs) is that there are restrictions on what you can do with them. You can make an array of a UDT. You cannot make a collection of a UDT.
In terms of .Net they're most similar to Struct.
There's a walkthrough of the basics here or here.
I had a similar scenario and used Dictionary by including a reference to Microsoft Scripting Runtime library in my VB6 project. This was suggested by a colleague of mine and worked really well.
Dim dictionary As New Dictionary
Dim index As Integer
dictionary.Add "Index1", "value for first index"
dictionary.Add "Index2", "value for second index"
'To get the value for a key
Debug.Print dictionary("Key1")
'To get the value for all keys
For index = 0 To UBound(dictionary.Keys)
Debug.Print dictionary.Keys(index) & "=" & dictionary(dictionary.Keys(index))
Next index
List Class? (see VB section):
http://msdn.microsoft.com/en-us/library/6sh2ey19#Y0
Dictionary Class?
http://msdn.microsoft.com/en-us/library/xfhwa508
You could use a Collection
dim c as new collection
c.add "a", "b"

does ruby have an elegant way to say array2 = some_lookup_method(array1)

I have an array short_code[] that contains an array of short product identifiers such as ["11111", "2222", "33333"]
I want to create a copy of the array that contains the corresponding 'long code' data:
long_code[i] = my_lookup_long_code(short_code[i])
While simple iteration is easy, I'm wondering, as a relative ruby newbie, what is the 'ruby way' to create an array which is a simply method() applied on every element in the original array?
You can use the map command, which will return a new array with the results of your code block:
long_code = short_code.map{ |code| my_lookup_long_code(code) }

Resources