Hi all virtual and real participants,
sorry about the delay and the necessity to clarify my intentions.
(1) VBScript Datatypes:
DiGiTAL.SkReAM is right to say that all VBScript variables (and expressions,
constants, and even functions -
Function funcNix( a )
WScript.Echo "funcNix called"
funcNix = "Nix"
End Function
Const csName = "ehvbs"
WScript.Echo 0, TypeName( 2 * 2 ), VarType( 2 * 2 )
WScript.Echo 1, TypeName( csName ), VarType( csName )
Set fncSelf = GetRef( "funcNix" )
WScript.Echo 2, TypeName( fncSelf ), VarType( fncSelf )
==>
0 Integer 2
1 String 8
2 Object 9
) are Variants. But as the functions TypeName() and VarType() show, each
VBScript entity belongs to some subtype (String, Integer, ...).
(2) Shortcomings of Join():
One context in which those subtypes show their (ugly) face is the
Join() function. If you send an array like
Array( 1, "test", New RegExp )
to Join(), you will get an error, because Join() can't handle variants
of subtype Object or Array to name just a few. The challenge is to write
a JoinX() function that will process an array with all - or at least the
most important - subtypes in a sensible way. To exercise/test/prove this
JoinX() function you need an array of sample data. That's why I included
"Create an Array aTest with at least one sample for each possible subtype"
a part of the challenge.
Even for variable(subtype)s that are handled by Join() it could be asked
whether the output format is desirable for all purposes. If you are interested
to see what's in your arrays, Join() will disguise differences:
WScript.Echo 1, Join( Array( 1, Empty, 3 ), "," )
WScript.Echo 2, Join( Array( 1, "", 3 ), "," )
WScript.Echo 3, Join( Array( 1, "2", 3 ), "," )
WScript.Echo 4, Join( Array( 1, 2 , 3 ), "," )
WScript.Echo 5, Join( Array( 1, "2,3" ), "," )
output:
1 1,,3
2 1,,3
3 1,2,3
4 1,2,3
5 1,2,3
If your ultimate aim is some kind of serialisation, Join() will help you
in some very few/resticted cases:
Dim aIntsOnly : aIntsOnly = Array( 1, 2, 3 )
Dim sIntsOnly : sIntsOnly = Join( aIntsOnly, "," )
WScript.Echo 6, sIntsOnly
sIntsOnly = "Array( " + sIntsOnly + ")"
aIntsOnly = Eval( sIntsOnly )
WScript.Echo 7, Join( aIntsOnly, "," )
output:
6 1,2,3
7 1,2,3
but fail under all other circumstances.
Things like that make me wish for a better Join().
(3) No restrictions:
My remarks concerning ginolard's use of the .NET ArrayList weren't meant
to exclude this - or any other - approach to a solution. If it works, it's
ok. But I'd consider a JoinX() function better that wouldn't involve a
transformation from VBScript Array to .NET ArrayList, especially as ginolard
himself pointed out problems with the Currency subtype. But this personal
preference won't rule out that a poll/some other form of mutual agreement
declares ginolard's solution (enhanced by a JoinX() function) the winner.
As I said, if the .Net ArrayList will make join part easier, then I would
even take back my sceptic words about ginolard's idea.
(4) Getting the data:
I believe we got enough examples of how to fill an array with sample data
to enable everybody interested in this challenge to concentrate on the
JoinX() function. There may be some details - e.g. the Err object - to discuss
and while we probably can agree on excluding the Decimal subtype (you can't
create such a variable anyway) we may have to ponder, whether or not DataObjects
can/should be handled.
(5) Recursive JoinX():
The JoinX() function has to recursive to handle Arrays (and Dictionaries?,
thanks for the remainder, ebgreen!). If you compare ebgreen's recursive dictionary
lister to the approaches proposed by DiGiTAL.SkReAM and dm_4ever, you will
see an important difference: While ebgreen's code keeps the hierarchical
structure of the input array, both DiGiTAL.SkReAM's and dm_4ever's functions
'flatten' the structure (like the arguments to a sub in Perl are 'flattened'
in one continous array). Code like this
Dim aOne : aOne = Array( 1, 2, 3, 4 )
Dim aTwo : aTwo = Array( Array( 1, 2 ), Array( 3, 4 ) )
WScript.Echo "aOne:", JoinX( aOne, "|" )
WScript.Echo "aTwo:", JoinX( aTwo, "|" )
will get you output like this:
cscript digitalscream01.vbs
aOne: 1|2|3|4
aTwo: 1|2|3|4
cscript dm_4ever01.vbs
aOne: 1|2|3|4
aTwo: 1|2|3|4
but
Dim aOne : aOne = Array( 1, 2, 3, 4 )
Dim aTwo : aTwo = Array( Array( 1, 2 ), Array( 3, 4 ) )
WScript.Echo "aOne: ----------"
ShowArray aOne, "aOne", 0
WScript.Echo "aTwo: ----------"
ShowArray aTwo, "aTwo", 0
results in:
cscript ebgreen01.vbs
aOne: ----------
aOne(0) => 1
aOne(1) => 2
aOne(2) => 3
aOne(3) => 4
aTwo: ----------
aTwo(0)(0) => 1
aTwo(0)(1) => 2
aTwo(1)(0) => 3
aTwo(1)(1) => 4
Which way is 'better'? Do we have to decide? Perl's flattening of arguments to
a sub is a successful design decision, but Data::Dumper works hard to keep the
internal structure of the dumped variables. Serialisation would be impossible
with a flattening version of JoinX(), but do we need it?
(6) The nits to pick:
I posted this challenge, because I'm in need of a suitable JoinX() function
and I hoped to get some insight/ideas/code from you. This hope is being
fullfilled. Thanks for all the past - and future - efforts you put into this.
My critical remarks are intended as arguments/questions/suggestions in a
peer to peer discussion, not as judgements of an arrogant teacher about
the pupils homework.
dm_4ever's last proprosal is 'nearly there', but I don't like the treatment
of Empty. If Null gets <Null> then Empty should get <Empty> - and what about
Nothing? Perhaps adding suitable delimiters - string => "string", date =>
#date#, array => Array(JoinX(array)) - would improve the lucidity of the
output, handle the recursion/structure problem and pave the way for seriali-
sation? I'm wondering, whether DiGiTAL.SkReAM's tactic of using a dictionary
to collect the reworked items of the original array to feed the keys to
VBScript's own Join() [nice trick, by the way] is the most efficient in this
special case. Couldn't you use an array instead because you can
ReDim aTmp( UBound( arrInput )
before the loop - no need for dynamic growing here? Neither for strOutput
aTmp( intElem ) = <whatever>
and at the end just
JoinX = Join(aTmp, strSeperator)
Surely putting a value in an existing array slot is more efficient than
adding a new item to a dictionary?
DiGiTAL.SkReAM nearly solved the problem although he was 'completely lost as
to what' I was looking for. Just a bit more work in this ELSE part
Else
sJoined = thing
End If
to handle the difficult subtypes ( Empty, Null, ... ). Perhaps a crossbred
with dm_4ever's SELECT
Else
Select Case True
Case TypeName( thing ) = "Null"
sJoined = "<NULL>"
Case IsObject( thing )
sJoined = TypeName( thing )
....
Case Else
sJoined = CStr( thing )
End Select
End If
Thanks to ebgreen for demonstrating the keeping of the data structure in
his recursive directory lister. I really hope, you can spare some time to
look at this again - the SELECT
Select Case TypeName(oA(i))
Case "Integer"
WScript.Echo strSpace & strName & "(" & i & ") => " & oA(i)
could be combined with DiGiTAL.SkReAM's or dm_4ever's code for collecting
and joining the elements.
ginolard's contribution lacks the joining part of the problem. Perhaps
he could outrun everybody - if he can harness the .NET String.Join( sSep,
aList ) function.
Thanks again for all the input - and my apologies for the number of lines!
ehvbs