Is there a way to resize an array from inside a procedure? Using Reflection maybe?

  • Thread starter Thread starter John Brock
  • Start date Start date
J

John Brock

I want to write a VB.NET subroutine that will take an input array
of arbitrary type and resize it. Here is a trivial example of what
I am trying to do:

'Increase the length of an arbitrary input array by one, using ReDim.
Public Sub TryToReDim(ByRef ArrayIn As Object())
ReDim Preserve ArrayIn(ArrayIn.Length)
End Sub

Actually what I really want -- because VB is so braindead when it
comes to array manipulation -- is a Perl style Splice() routine,
which of course would need to be able to resize the input array.
I have a routine like that that works fine in VBA, but when I try
to translate it to VB.NET I am tripped up by casting exceptions.
Here is an example of the problem:

Dim xxx() as String = {"aaa", "bbb", "ccc"}
TryToReDim(xxx)

This throws the following exception:

InvalidCastException occurred
Unable to cast object of type 'System.Object[]' to type 'System.String[]'.

The problem is that, although the input variable ArrayIn is of type
String[], ReDim, instead of redimensioning the array that was
passed, creates a new, local array of type Object[], which then
can't be assigned back to the original String[] input variable.

So what can I do? For example, is there some way that, instead of
using ReDim, I could examine the input array, determine its type,
and create a properly sized local ArrayIn array of the same type,
which could eventually be copied back without problems? I know
that there is a such thing as Reflection in .NET, but it's not
clear to me how that would apply in this case.

For that matter, does VB.NET already have the equivalent of Splice()
somewhere, which, for example, would allow me to insert an element
into the middle of an array, without having to jump though hoops?
I have never understood why it is so awkward to manipulate arrays
in VBA and VB.NET! Does anyone have any idea why, after all this
time, this hasn't been fixed?
 
"VB is so braindead when it comes to array manipulation"

Just one question did you ever noticed the array class , in the .Net
Framework ?
imho
VB and C# are masters with array manipulation


http://msdn.microsoft.com/en-us/library/system.array.aspx

resizing with the array class

http://msdn.microsoft.com/en-us/library/bb348051.aspx

and lots and lots more

The exception that you encounter is obvious , you might be bether of with
generics in your case


HTH

Michel Posseth



John Brock said:
I want to write a VB.NET subroutine that will take an input array
of arbitrary type and resize it. Here is a trivial example of what
I am trying to do:

'Increase the length of an arbitrary input array by one, using ReDim.
Public Sub TryToReDim(ByRef ArrayIn As Object())
ReDim Preserve ArrayIn(ArrayIn.Length)
End Sub

Actually what I really want -- because VB is so braindead when it
comes to array manipulation -- is a Perl style Splice() routine,
which of course would need to be able to resize the input array.
I have a routine like that that works fine in VBA, but when I try
to translate it to VB.NET I am tripped up by casting exceptions.
Here is an example of the problem:

Dim xxx() as String = {"aaa", "bbb", "ccc"}
TryToReDim(xxx)

This throws the following exception:

InvalidCastException occurred
Unable to cast object of type 'System.Object[]' to type 'System.String[]'.

The problem is that, although the input variable ArrayIn is of type
String[], ReDim, instead of redimensioning the array that was
passed, creates a new, local array of type Object[], which then
can't be assigned back to the original String[] input variable.

So what can I do? For example, is there some way that, instead of
using ReDim, I could examine the input array, determine its type,
and create a properly sized local ArrayIn array of the same type,
which could eventually be copied back without problems? I know
that there is a such thing as Reflection in .NET, but it's not
clear to me how that would apply in this case.

For that matter, does VB.NET already have the equivalent of Splice()
somewhere, which, for example, would allow me to insert an element
into the middle of an array, without having to jump though hoops?
I have never understood why it is so awkward to manipulate arrays
in VBA and VB.NET! Does anyone have any idea why, after all this
time, this hasn't been fixed?
 
John Brock said:
I want to write a VB.NET subroutine that will take an input array of
arbitrary type and resize it.

It is not possible to resize an array. Not in VB.Net, not in any other .Net
Framework based language. Read also the documentation on Redim Preserve. It
creates a new array as you've already pointed out below on your own.

Use one of the many other helpful collections:
http://msdn.microsoft.com/en-us/library/7y3x785f.aspx
(maybe before all "commonly used collections")
Dim xxx() as String = {"aaa", "bbb", "ccc"}
TryToReDim(xxx)

This throws the following exception:

No, It can not be compiled. Enable Option Strict On! "It might work" is not
good enough.


Armin
 
John Brock said:
For that matter, does VB.NET already have the equivalent of Splice()
somewhere, which, for example, would allow me to insert an element
into the middle of an array, without having to jump though hoops?
I have never understood why it is so awkward to manipulate arrays
in VBA and VB.NET! Does anyone have any idea why, after all this
time, this hasn't been fixed?

The problem is not VB, it's your approach. The array is a fixed size block
of memory as Armin pointed out. You should be using one of the many
collection classes if you want to insert items in the middle of a list. Most
likely the List class is what you need as it has an insert method. The list
class is generic so can be created as type string

Dim list as New List(Of String)
list.add("Aa")
list.add("ba")
list.add("va")
list.add("da")
list.Insert("aaaa", 3")

Michael
 
John Brock said:
I want to write a VB.NET subroutine that will take an input array
of arbitrary type and resize it. Here is a trivial example of what
I am trying to do:

'Increase the length of an arbitrary input array by one, using ReDim.
Public Sub TryToReDim(ByRef ArrayIn As Object())
ReDim Preserve ArrayIn(ArrayIn.Length)
End Sub

Actually what I really want -- because VB is so braindead when it
comes to array manipulation -- is a Perl style Splice() routine,
which of course would need to be able to resize the input array.
I have a routine like that that works fine in VBA, but when I try
to translate it to VB.NET I am tripped up by casting exceptions.
Here is an example of the problem:

Dim xxx() as String = {"aaa", "bbb", "ccc"}
TryToReDim(xxx)

This throws the following exception:

InvalidCastException occurred
Unable to cast object of type 'System.Object[]' to type 'System.String[]'.

The problem is that, although the input variable ArrayIn is of type
String[], ReDim, instead of redimensioning the array that was
passed, creates a new, local array of type Object[], which then
can't be assigned back to the original String[] input variable.

So what can I do? For example, is there some way that, instead of
using ReDim, I could examine the input array, determine its type,
and create a properly sized local ArrayIn array of the same type,
which could eventually be copied back without problems? I know
that there is a such thing as Reflection in .NET, but it's not
clear to me how that would apply in this case.

For that matter, does VB.NET already have the equivalent of Splice()
somewhere, which, for example, would allow me to insert an element
into the middle of an array, without having to jump though hoops?
I have never understood why it is so awkward to manipulate arrays
in VBA and VB.NET! Does anyone have any idea why, after all this
time, this hasn't been fixed?
Brain dead? As you've been told, you need exposure to the various
collections classes in .NET.
It's easy to convert your array to a List<>, then do any resizing needed,
then convert the List<> back to an array if you must have it as an array.
Look up the collections classes in MSDN and observe all the management
methods available.
 
I want to write a VB.NET subroutine that will take an input array
of arbitrary type and resize it. Here is a trivial example of what
I am trying to do:

'Increase the length of an arbitrary input array by one, using ReDim.
Public Sub TryToReDim(ByRef ArrayIn As Object())
ReDim Preserve ArrayIn(ArrayIn.Length)
End Sub

Actually what I really want -- because VB is so braindead when it
comes to array manipulation -- is a Perl style Splice() routine,
which of course would need to be able to resize the input array.
I have a routine like that that works fine in VBA, but when I try
to translate it to VB.NET I am tripped up by casting exceptions.
Here is an example of the problem:

Dim xxx() as String = {"aaa", "bbb", "ccc"}
TryToReDim(xxx)

This throws the following exception:

InvalidCastException occurred
Unable to cast object of type 'System.Object[]' to type 'System.String[]'.

The problem is that, although the input variable ArrayIn is of type
String[], ReDim, instead of redimensioning the array that was
passed, creates a new, local array of type Object[], which then
can't be assigned back to the original String[] input variable.

So what can I do? For example, is there some way that, instead of
using ReDim, I could examine the input array, determine its type,
and create a properly sized local ArrayIn array of the same type,
which could eventually be copied back without problems? I know
that there is a such thing as Reflection in .NET, but it's not
clear to me how that would apply in this case.

For that matter, does VB.NET already have the equivalent of Splice()
somewhere, which, for example, would allow me to insert an element
into the middle of an array, without having to jump though hoops?
I have never understood why it is so awkward to manipulate arrays
in VBA and VB.NET! Does anyone have any idea why, after all this
time, this hasn't been fixed?

Something like this:

Option Explicit On
Option Strict On

Imports System
Imports System.Collections.Generic

Module Module1

Sub Main()
Dim arr() As String = {"aaa", "ccc"}

Console.WriteLine("==================== Before ======================")
Array.ForEach(arr, AddressOf PrintElement)
Console.WriteLine("==================================================")

arr = Splice(Of String)(arr, "bbb", 1)

Console.WriteLine("===================== After ======================")
Array.ForEach(arr, AddressOf PrintElement)
Console.WriteLine("==================================================")

End Sub

Sub PrintElement(ByVal element As String)
Console.WriteLine(element)
End Sub

Function Splice(Of T)(ByVal arr() As T, ByVal item As T, ByVal index As Integer) As T()
Dim l As New List(Of T)(arr)
l.Insert(index, item)
Return l.ToArray()
End Function
End Module

That implementation of splice, is well very naive - there are no error checks
or anything :) But, as others have already said, I think what you really should
be looking at are Generics and the Generic colleciton classes. They are much
more flexible.
 
Tom Shelton said:
I want to write a VB.NET subroutine that will take an input array
of arbitrary type and resize it. Here is a trivial example of what
I am trying to do:

'Increase the length of an arbitrary input array by one, using ReDim.
Public Sub TryToReDim(ByRef ArrayIn As Object())
ReDim Preserve ArrayIn(ArrayIn.Length)
End Sub

Actually what I really want -- because VB is so braindead when it
comes to array manipulation -- is a Perl style Splice() routine,
which of course would need to be able to resize the input array.
I have a routine like that that works fine in VBA, but when I try
to translate it to VB.NET I am tripped up by casting exceptions.
Here is an example of the problem:

Dim xxx() as String = {"aaa", "bbb", "ccc"}
TryToReDim(xxx)

This throws the following exception:

InvalidCastException occurred
Unable to cast object of type 'System.Object[]' to type 'System.String[]'.

The problem is that, although the input variable ArrayIn is of type
String[], ReDim, instead of redimensioning the array that was
passed, creates a new, local array of type Object[], which then
can't be assigned back to the original String[] input variable.

So what can I do? For example, is there some way that, instead of
using ReDim, I could examine the input array, determine its type,
and create a properly sized local ArrayIn array of the same type,
which could eventually be copied back without problems? I know
that there is a such thing as Reflection in .NET, but it's not
clear to me how that would apply in this case.

For that matter, does VB.NET already have the equivalent of Splice()
somewhere, which, for example, would allow me to insert an element
into the middle of an array, without having to jump though hoops?
I have never understood why it is so awkward to manipulate arrays
in VBA and VB.NET! Does anyone have any idea why, after all this
time, this hasn't been fixed?
Something like this:

Option Explicit On
Option Strict On

Imports System
Imports System.Collections.Generic

Module Module1

Sub Main()
Dim arr() As String = {"aaa", "ccc"}

Console.WriteLine("==================== Before ======================")
Array.ForEach(arr, AddressOf PrintElement)
Console.WriteLine("==================================================")

arr = Splice(Of String)(arr, "bbb", 1)

Console.WriteLine("===================== After ======================")
Array.ForEach(arr, AddressOf PrintElement)
Console.WriteLine("==================================================")

End Sub

Sub PrintElement(ByVal element As String)
Console.WriteLine(element)
End Sub

Function Splice(Of T)(ByVal arr() As T, ByVal item As T, ByVal index As Integer) As T()
Dim l As New List(Of T)(arr)
l.Insert(index, item)
Return l.ToArray()
End Function
End Module

That implementation of splice, is well very naive - there are no error checks
or anything :) But, as others have already said, I think what you really should
be looking at are Generics and the Generic colleciton classes. They are much
more flexible.

Thanks Tom, you gave me the clue I needed. (Thanks also to everyone
else who offered suggestions). Here is what I came up with for my
simple example:

'Add one element to the end of an arbitrary input array using ReDim.
Public Function TryToReDim(Of T)(ByRef ArrayIn As T()) As T()
ReDim Preserve ArrayIn(ArrayIn.Length)
'Alternatively: Array.Resize(Of T)(ArrayIn, ArrayIn.Length + 1)
TryToReDim = ArrayIn
End Function

...

Dim xxx() as String = {"aaa", "bbb", "ccc"}
TryToReDim(xxx)
'xxx.Length now = 4.

Notice that I made it a function this time, just to be sure I could.
(I'm using .NET Framework 2.0, BTW).

It's still not entirely clear to me why this works -- I need to do
some reading, but my books don't have much on generics, and the
online documention is a bit fragmented. In particular, at first
I thought that if I used "Of" in my function definition then my
function call would have to look like this:

TryToReDim(Of String)(xxx)

This of course kind of defeats the purpose, since I would have to
explicitly name the type every time I used the function, which
would be a pain, and not always even possible. But it turns out
that if I omit the Of clause when I call the function .NET infers
the value of T from ArrayIn! Makes sense I suppose, but I'd really
like to see the documentation that explains what is going on.

Anyway, I got my Perl style Splice() function working. I'm going
to test it for a day or so and then post it here. Hopefully someone
else might find it useful (or maybe find mistakes). Thanks again!
 
PvdG42 said:
Brain dead? As you've been told, you need exposure to the various
collections classes in .NET.
It's easy to convert your array to a List<>, then do any resizing needed,
then convert the List<> back to an array if you must have it as an array.
Look up the collections classes in MSDN and observe all the management
methods available.

Um, I think "braindead" is in fact the appropriate word here. Your
suggestion will certainly work, but it's a pain. In fact it is
actually an perfect example of just the sort of hoop I've been
hoping to avoid having to jump through when it comes time to do
anything with arrays.

It's always possible I've missed something though. An example
might be helpful! Let's say that, for the array below, I wanted
to insert an additional element, "xxx" perhaps, in the second
position. Is there a one line VB.NET statement that will do this?
(If possible the statement should be general, so that it will work
equally well if I want to use it for an array of Integers, or other
types).

Dim xxx() as String = {"aaa", "bbb", "ccc"}

This is something which is trivially easy in Perl, and, IMO, ought
to be easy in any well designed language. If you can't come up
with a one line example, then a two or three line example would
also be welcome. Actually, I'd be interested in seeing how you
would handle this situation if it came up in your own code.

Don't tell me though that I ought to be using lists instead of
arrays. For one thing, I might be dealing with existing code, and
not have that option. In any case, unless you are saying arrays
in .NET are totally deprecated and should never be used, there is
always the possibility that you will find yourself using arrays
because they are the best choice for a particular situation, and
then finding that you need to insert elements into the middle of
those arrays. So how -- in a general way -- does one do that?

If in fact there is no easy way I'd still be interested in knowing
why. It's possible there is some deep legacy issue which prevents
Microsoft from making the obvious additions to the language, but
I'd be interested in knowing what that issue is.
 
'Add one element to the end of an arbitrary input array using ReDim.
Public Function TryToReDim(Of T)(ByRef ArrayIn As T()) As T()
ReDim Preserve ArrayIn(ArrayIn.Length)
'Alternatively: Array.Resize(Of T)(ArrayIn, ArrayIn.Length + 1)
TryToReDim = ArrayIn
End Function

...

Dim xxx() as String = {"aaa", "bbb", "ccc"}
TryToReDim(xxx)
'xxx.Length now = 4.


No no. Our message was: If you have a dynamic count of items, DO NOT use an
Array!

Maybe it's already been mentioned, but then again: Do you know what Redim
Preserve does? It creates a new array, copies all data into the new array,
then assigns the new array to the "redimmed" variable. That's time
consuming. Arrays can not be resized because growing arrays would overwrite
all data behind, and shrinking arrays would create memory leaks. All the
dynamic lists use optimized memory management made for a dynamic number of
itmes, and you don't have to care about it at all whenever you add or remove
items. So, do not use an array (usually).

Notice that I made it a function this time, just to be sure I could.
(I'm using .NET Framework 2.0, BTW).

It's still not entirely clear to me why this works -- I need to do
some reading, but my books don't have much on generics, and the
online documention is a bit fragmented. In particular, at first
I thought that if I used "Of" in my function definition then my
function call would have to look like this:

TryToReDim(Of String)(xxx)

This of course kind of defeats the purpose, since I would have to
explicitly name the type every time I used the function, which
would be a pain, and not always even possible. But it turns out
that if I omit the Of clause when I call the function .NET infers
the value of T from ArrayIn!

How can you omit "Of"? It wasn't compilable then. Even with Option Strict
Off. Or do you mean if you also change "T" to "String"?
Makes sense I suppose, but I'd really
like to see the documentation that explains what is going on.

In what way don't you understand what's going on?

The argument is passed "Byref". This means, if you call the function, a
REFerence (= ByREF) to the passed variable, xxx in this case, is passed.
Inside the function, Redim Preserve does what I'v described above. As you
"Redim Preserve ArrayIn", the new array is stored in the location that the
Argument ArrayIn points to. That location is the variable xxx in the calling
function. Consequently, after the function returns, xxx has a new array with
4 items.

I hope you have a basic knowledge of what a stack is and how arguments are
passed on the stack. This is basic(!) knowledge you _must_ know before you
write your first function at all. If that's not the problem, I haven't said
anything. ;-)


Armin
 
John Brock said:
Is there a one line VB.NET statement that will do this?
(If possible the statement should be general, so that it will work
equally well if I want to use it for an array of Integers, or other
types).

Dim xxx() as String = {"aaa", "bbb", "ccc"}

This is something which is trivially easy in Perl, and, IMO, ought
to be easy in any well designed language. If you can't come up
with a one line example, then a two or three line example would
also be welcome. Actually, I'd be interested in seeing how you
would handle this situation if it came up in your own code.

I'm sure you are right that this is easy with Perl. I don't know Perl.
However, as there are other ways to do what you want in the .Net world, why
do you insist on doing it with an array? It is as if I'd use Perl and insist
on using a List(Of) there but it doesn't exist.

You've simply chosen the wrong type for your purposes.


Armin
 
You've simply chosen the wrong type for your purposes.

I mean, you have made a wrong translation. "Array" in the Perl language
translated to VB.Net is "List(Of)". You translated it to "Array". That's
obviously wrong.


Armin
 
No no. Our message was: If you have a dynamic count of items, DO NOT use an
Array!

Maybe it's already been mentioned, but then again: Do you know what Redim
Preserve does? It creates a new array, copies all data into the new array,
then assigns the new array to the "redimmed" variable. That's time
consuming. Arrays can not be resized because growing arrays would overwrite
all data behind, and shrinking arrays would create memory leaks. All the
dynamic lists use optimized memory management made for a dynamic number of
itmes, and you don't have to care about it at all whenever you add or remove
items. So, do not use an array (usually).



How can you omit "Of"? It wasn't compilable then. Even with Option Strict
Off. Or do you mean if you also change "T" to "String"?

Option Infer On? I wonder if the OP is using 2008, but targeting the 2.0
framework.
 
Um, I think "braindead" is in fact the appropriate word here. Your
suggestion will certainly work, but it's a pain. In fact it is
actually an perfect example of just the sort of hoop I've been
hoping to avoid having to jump through when it comes time to do
anything with arrays.

Are you saying learning about the collection classes are unneeded hoops?
It's always possible I've missed something though. An example
might be helpful! Let's say that, for the array below, I wanted
to insert an additional element, "xxx" perhaps, in the second
position. Is there a one line VB.NET statement that will do this?
(If possible the statement should be general, so that it will work
equally well if I want to use it for an array of Integers, or other
types).

Dim xxx() as String = {"aaa", "bbb", "ccc"}

This is something which is trivially easy in Perl, and, IMO, ought
to be easy in any well designed language. If you can't come up
with a one line example, then a two or three line example would
also be welcome. Actually, I'd be interested in seeing how you
would handle this situation if it came up in your own code.

I believe that perl implements it's "arrays" internally as a linked list. Not
a C-style fixed size chunk of memory. In .NET, Arrays are essentially that -
fixed chunk of memory.

If you insist on using arrays rather then the collection classes (such as
List(Of T)) then you will want to understand this implementation so that you
can efficiently work with them. First, you should understand what ReDim does,
it creates a new array - basically it allocates a new chunk of memory, and if
you specify preserve, then it copies the values of the old array to the new
array. That's fine and good, but is very inefficient if you are going to make
a lot of calls to ReDim. So, you will want to grow the array more then needed
to keep the calls to ReDim to a minimum. The common algorith is to increase
the size by 10% of the current size with each allocation.

Of course, this posess a problem... Because the size of the array and the
number of elements are out of sync - so, you probably will want to introduce
a variable to keep track of the actual number of values stored in the array.

Another consideration is the addition and removal of items. Generally, that
means shifting things around - either up or down in the array, setting the
unused slots to nothing, and then updating your element counter...

It's a lot of work, so, to keep from making a mistake, you might want to wrap
this all in a class that can handle the management of the array. Then provide
a method to get an actual array when you need it.

Oh, or you could just use List(Of T) - because that's exactly what it does.
List(Of T) is a wrapper for an array, that handles all of the managment
details for you.
Don't tell me though that I ought to be using lists instead of
arrays.

See above - and it's trivial to go to and from a list to an array. List(Of T)
takes an IEnumerable(Of T) in one of it's constructors (which System.Array
implements) and it has a .ToArray method.
For one thing, I might be dealing with existing code, and
not have that option.

See above.
In any case, unless you are saying arrays
in .NET are totally deprecated and should never be used, there is
always the possibility that you will find yourself using arrays
because they are the best choice for a particular situation, and
then finding that you need to insert elements into the middle of
those arrays. So how -- in a general way -- does one do that?

If in fact there is no easy way I'd still be interested in knowing
why. It's possible there is some deep legacy issue which prevents
Microsoft from making the obvious additions to the language, but
I'd be interested in knowing what that issue is.

The fact that arrays in .NET are arrays, and not linked lists? Arrays are
very effiecient - if they don't need to be changed much. If the data is not
static in it's size, then managing an array in an efficient manner becomes a
not so trivial exercise... Hence, System.Collections.ArrayList and
System.Collections.Generic.List(Of T) - you should prefere List(Of T) over
ArrayList in new code because of the improved functionality and the
performance benifits of generics.

HTH
 
John Brock said:
This is something which is trivially easy in Perl, and, IMO, ought
to be easy in any well designed language. If you can't come up
with a one line example, then a two or three line example would
also be welcome. Actually, I'd be interested in seeing how you
would handle this situation if it came up in your own code.

Perl isn't that strict a language, you might find a few things a little more
difficult in dot net.
Don't tell me though that I ought to be using lists instead of
arrays.

That is the best advice, if you want to ignore it then go ahead. What dot
net calls an array is a fixed block of memory and a fixed block of memory
has been difficult to resize since the beginning of computers. I think it's
actually better that dot net doesn't hide this from the programmer. IMO they
shouldn't even have had the redim or redim preserve statements, C# doesn't
have these which is for the better.

Michael
 
Michael C said:
That is the best advice, if you want to ignore it then go ahead.
What dot net calls an array is a fixed block of memory and a fixed
block of memory has been difficult to resize since the beginning of
computers. I think it's actually better that dot net doesn't hide
this from the programmer. IMO they shouldn't even have had the redim
or redim preserve statements, C# doesn't have these which is for the
better.

Regarding VB, Redim Preserve is probably only there for historic reasons
(VB6 etc) where there was no sort of List (apart form the untyped
Collection) - maybe also because upgraded projects should still work as good
as possible. Well, some people complained that not everything has been taken
over from VB6, some people complained that not everything has been dropped.
So, if you consider the situation that MSFT was in, I think they made a good
compromise. Anyway, that's history. BTW, I've never used Redim Preserve in
VB.Net (IIRC).


Armin
 
I have been frustrated for a while by the difficulty of manipulating
arrays in VBA and VB.NET -- in particular the awkwardness involved
in appending, prepending, or inserting new elements -- so to make
my life easier I wrote two functions that act something like Perl's
splice function. I wrote the VBA version first, and then a slightly
different version for VB.NET. Both versions are included here,
and they are both a little different from the Perl function, but
close enough that you should be able to make them functionally
identical without too much trouble if you happen to feel like it.

What the functions do is chop zero or more elements out of an input
array (which can be of any type) at a specified location, and then
splice zero or more new elements into the array at the same location,
all in a single function call. Note that in the VB.NET version
the input array isn't changed, while in the VBA version (like in the
Perl function) it is. (Note also that VB.NET version is the simpler
of the two, since I didn't have to worry about arrays having
something other than zero as a lower bound).

Here are some sample calls (VB.NET version):

Dim qqq() as String = {"aaa", "bbb", "ccc"} 'Input array for the examples.

ArraySplice(qqq, 0, 0, "xxx") '= {"xxx", "aaa", "bbb", "ccc"}
ArraySplice(qqq, -1, 0, "xxx") '= {"aaa", "bbb", "ccc", "xxx"}
ArraySplice(qqq, 1, 1) '= {"aaa", "ccc"}
ArraySplice(qqq, 1, 1, "xxx", "yyy") '= {"aaa", "xxx", "yyy", "ccc"}
ArraySplice(qqq, 5, 0, "xxx", "yyy") '= {"aaa", "bbb", "ccc", "", "", "xxx", "yyy"}

I'm sure others have done the same thing I've done here, but I
haven't come across any examples so far, so I'm posting this in
the hope that: 1) someone else might find these functions useful;
and 2) if there are any bugs in my code or mistakes in my approach
someone will notice and let me know. Thanks in advance for any
comments!

One thing in particular: I needed to use generics for the VB.NET
version of the function (.NET Framework 2.0, actually), and this
was new to me, so I especially want to make sure this works properly
for everyone, no matter what framework version they are using or
how they have their environment configured. I was a little surprised
that function calls could omit the "Of" clause, even though it is
part of the function definition! This certainly makes using the
function simpler, but it's it's not clear to me why this is OK, so
it would be be very reassuring if someone could point me to a piece
of documentation that officially says this is allowed.


====== Start of VB.NET version of ArraySplice =====


'A VB splice function inspired by the one in Perl, although with a couple of differences.
'A copy is made of "arrayIn", "length" elements beginning at index "offset" are removed and replaced
'with the contents of "list", and then the copy is returned. The original input array is not modified.
'If "offset" is negative then nothing is removed, and new elements are appended to the end of the array.
'(Note: there is no need to explicitly supply the "Of" clause when calling this function).
Public Function ArraySplice(Of T)( _
ByVal arrayIn As T(), _
ByVal offset As Integer, _
ByVal length As Integer, _
ByVal ParamArray list As Object()) As T()

arrayIn = arrayIn.Clone 'To protect the input array -- if you want arrayIn to be ByRef then remove.

'=== Test and adjust arguments.

If offset < 0 Then
offset = arrayIn.Length 'A negative offset means append to end.
ElseIf offset > arrayIn.Length Then
ReDim Preserve arrayIn(offset - 1) 'A large offset will extend the array.
End If

If length < 0 Then
Throw New ArgumentOutOfRangeException("length", "Value cannot be less than 0")
ElseIf length > arrayIn.Length - offset Then
length = arrayIn.Length - offset 'Can't remove more elements than you've got!
End If

'=== Now do the actual splicing.

If list.Length > length Then 'arrayIn has to get bigger.
Dim saveUBound As Integer = UBound(arrayIn)
ReDim Preserve arrayIn(UBound(arrayIn) + list.Length - length)

For i As Integer = saveUBound To offset + length Step -1
arrayIn(i + list.Length - length) = arrayIn(i)
Next
ElseIf list.Length < length Then 'arrayIn has to get smaller.
For i As Integer = offset + length To UBound(arrayIn)
arrayIn(i + list.Length - length) = arrayIn(i)
Next

ReDim Preserve arrayIn(UBound(arrayIn) + list.Length - length)
End If

'Copy new values to arrayIn.
For i As Integer = 0 To UBound(list)
arrayIn(offset + i) = list(i)
Next

ArraySplice = arrayIn
End Function


====== Start of VBA version of ArraySplice =====


'This is a Perl style Splice function (for dynamic arrays only!). It works by doing a ReDim on the input array and appropriately copying array elements.
'It differs from Perl in returning the altered input array, not the spliced elements, and in that a negative offset just means append to end, not count backwards from end.
'(Note that because the input array is altered by ReDim, but never replaced, LBound(arrayIn) remains unchanged).
Public Function ArraySplice(arrayIn As Variant, Optional ByVal offset As Integer = 0, Optional ByVal length As Integer = 0, Optional list As Variant) As Variant
If IsMissing(list) Then list = Array()

'=== First test, and possibly adjust, the input arguments.

If Not IsArray(arrayIn) Then Err.Raise 5
If Not IsArray(list) Then Err.Raise 5

If offset < 0 Then
offset = UBound(arrayIn) + 1 - LBound(arrayIn)
ElseIf offset > UBound(arrayIn) + 1 - LBound(arrayIn) Then
ReDim Preserve arrayIn(LBound(arrayIn) To LBound(arrayIn) + offset - 1)
End If

If length < 0 Then
Err.Raise 5 '(It would actually make sense to allow negative values for length, but it would just be too much trouble to implement for too little value).
ElseIf length > UBound(arrayIn) + 1 - (LBound(arrayIn) + offset) Then
length = UBound(arrayIn) + 1 - (LBound(arrayIn) + offset) 'Can't remove more than you've got, so adjust the value of length,
End If

Dim arrayInLen As Integer, listLen As Integer, i As Integer
arrayInLen = UBound(arrayIn) + 1 - LBound(arrayIn)
listLen = UBound(list) + 1 - LBound(list)

'If the output should be a zero length array then throw an error, because there is no reliable way to make this happen in VBA.
'(You can't use ReDim to create a zero length array, and you can't even always set arrayIn = Array()).
If UBound(arrayIn) + listLen - length < LBound(arrayIn) Then Err.Raise 5

'=== Now we can do the actual splicing.

'Expand or reduce the size of the array, and shift the elements not being removed appropriately.
If listLen > length Then
Dim originalUBound As Integer
originalUBound = UBound(arrayIn)

ReDim Preserve arrayIn(LBound(arrayIn) To UBound(arrayIn) + listLen - length)

For i = originalUBound To LBound(arrayIn) + offset + length Step -1
arrayIn(i + listLen - length) = arrayIn(i)
Next
ElseIf listLen < length Then
For i = LBound(arrayIn) + offset + length To UBound(arrayIn)
arrayIn(i + listLen - length) = arrayIn(i)
Next

ReDim Preserve arrayIn(LBound(arrayIn) To UBound(arrayIn) + listLen - length)
End If

For i = LBound(list) To UBound(list)
arrayIn(LBound(arrayIn) + offset + i - LBound(list)) = list(i)
Next

ArraySplice = arrayIn
End Function


====== End of ArraySplice versions =====
 
Armin Zingler said:
Regarding VB, Redim Preserve is probably only there for historic reasons
(VB6 etc) where there was no sort of List (apart form the untyped
Collection) - maybe also because upgraded projects should still work as
good as possible. Well, some people complained that not everything has
been taken over from VB6, some people complained that not everything has
been dropped.

Hehe, I know what you mean, they can't win either way :-) I'm definately
from the camp that would like all the VB6 stuff gone, which is probably why
I prefer C# because there is more of a departure from VB6.
So, if you consider the situation that MSFT was in, I think they made a
good compromise. Anyway, that's history. BTW, I've never used Redim
Preserve in VB.Net (IIRC).

I don't think VB6 ported over to dot net well at all so leaving redim
preserve in or out wouldn't have made that much difference. IMO of course
:-)

Michael
 
John Brock said:
I have been frustrated for a while by the difficulty of manipulating
arrays in VBA and VB.NET -- in particular the awkwardness involved
in appending, prepending, or inserting new elements -- so to make
my life easier I wrote two functions that act something like Perl's
splice function. I wrote the VBA version first, and then a slightly
different version for VB.NET. Both versions are included here,
and they are both a little different from the Perl function, but
close enough that you should be able to make them functionally
identical without too much trouble if you happen to feel like it.

You really need to stop trying to write perl code in dot net and VBA. Arrays
are hard to resize for a good reason, this goes down to how the hardware
works. They have the advantage of being super efficient but this comes at
the expense of some restrictions. If you want the flexibility then use the
list class. I really cannot accept at all that you are stuck using the array
because of some existing code. It will either be very easy to change the
existing code or very easy to cast the array to a list when needed (1 line
of code).

Several people here have given you good advice and you choose to ignore it.
Inserting an element in a list is one line of code but you insist on writing
pages of code to do it the wrong way. If you ask a question here you should
take the GOOD advice you are given. If you ignore it then there isn't much
point asking the question.

Michael
 
I have been frustrated for a while by the difficulty of manipulating
arrays in VBA and VB.NET -- in particular the awkwardness involved
in appending, prepending, or inserting new elements -- so to make
my life easier I wrote two functions that act something like Perl's
splice function. I wrote the VBA version first, and then a slightly
different version for VB.NET. Both versions are included here,
and they are both a little different from the Perl function, but
close enough that you should be able to make them functionally
identical without too much trouble if you happen to feel like it.

What the functions do is chop zero or more elements out of an input
array (which can be of any type) at a specified location, and then
splice zero or more new elements into the array at the same location,
all in a single function call. Note that in the VB.NET version
the input array isn't changed, while in the VBA version (like in the
Perl function) it is. (Note also that VB.NET version is the simpler
of the two, since I didn't have to worry about arrays having
something other than zero as a lower bound).

Here are some sample calls (VB.NET version):

Dim qqq() as String = {"aaa", "bbb", "ccc"} 'Input array for the examples.

ArraySplice(qqq, 0, 0, "xxx") '= {"xxx", "aaa", "bbb", "ccc"}
ArraySplice(qqq, -1, 0, "xxx") '= {"aaa", "bbb", "ccc", "xxx"}
ArraySplice(qqq, 1, 1) '= {"aaa", "ccc"}
ArraySplice(qqq, 1, 1, "xxx", "yyy") '= {"aaa", "xxx", "yyy", "ccc"}
ArraySplice(qqq, 5, 0, "xxx", "yyy") '= {"aaa", "bbb", "ccc", "", "", "xxx", "yyy"}

I'm sure others have done the same thing I've done here, but I
haven't come across any examples so far, so I'm posting this in
the hope that: 1) someone else might find these functions useful;
and 2) if there are any bugs in my code or mistakes in my approach
someone will notice and let me know. Thanks in advance for any
comments!

One thing in particular: I needed to use generics for the VB.NET
version of the function (.NET Framework 2.0, actually), and this
was new to me, so I especially want to make sure this works properly
for everyone, no matter what framework version they are using or
how they have their environment configured. I was a little surprised
that function calls could omit the "Of" clause, even though it is
part of the function definition! This certainly makes using the
function simpler, but it's it's not clear to me why this is OK, so
it would be be very reassuring if someone could point me to a piece
of documentation that officially says this is allowed.


====== Start of VB.NET version of ArraySplice =====


'A VB splice function inspired by the one in Perl, although with a couple of differences.
'A copy is made of "arrayIn", "length" elements beginning at index "offset" are removed and replaced
'with the contents of "list", and then the copy is returned. The original input array is not modified.
'If "offset" is negative then nothing is removed, and new elements are appended to the end of the array.
'(Note: there is no need to explicitly supply the "Of" clause when calling this function).
Public Function ArraySplice(Of T)( _
ByVal arrayIn As T(), _
ByVal offset As Integer, _
ByVal length As Integer, _
ByVal ParamArray list As Object()) As T()

arrayIn = arrayIn.Clone 'To protect the input array -- if you want arrayIn to be ByRef then remove.

'=== Test and adjust arguments.

If offset < 0 Then
offset = arrayIn.Length 'A negative offset means append to end.
ElseIf offset > arrayIn.Length Then
ReDim Preserve arrayIn(offset - 1) 'A large offset will extend the array.
End If

If length < 0 Then
Throw New ArgumentOutOfRangeException("length", "Value cannot be less than 0")
ElseIf length > arrayIn.Length - offset Then
length = arrayIn.Length - offset 'Can't remove more elements than you've got!
End If

'=== Now do the actual splicing.

If list.Length > length Then 'arrayIn has to get bigger.
Dim saveUBound As Integer = UBound(arrayIn)
ReDim Preserve arrayIn(UBound(arrayIn) + list.Length - length)

For i As Integer = saveUBound To offset + length Step -1
arrayIn(i + list.Length - length) = arrayIn(i)
Next
ElseIf list.Length < length Then 'arrayIn has to get smaller.
For i As Integer = offset + length To UBound(arrayIn)
arrayIn(i + list.Length - length) = arrayIn(i)
Next

ReDim Preserve arrayIn(UBound(arrayIn) + list.Length - length)
End If

'Copy new values to arrayIn.
For i As Integer = 0 To UBound(list)
arrayIn(offset + i) = list(i)
Next

ArraySplice = arrayIn
End Function

<snip> VBA </snip>

Yikes... Dude - List(Of T).
 
John Brock wrote:

Actually what I really want -- because  VB is so braindead when it
comes to array manipulation -- is a Perl style Splice() routine
<snip>

And then, in another message:

'Add one element to the end of an arbitrary input array using ReDim.
Public Function TryToReDim(Of T)(ByRef ArrayIn As T()) As T()
ReDim Preserve ArrayIn(ArrayIn.Length)
'Alternatively: Array.Resize(Of T)(ArrayIn, ArrayIn.Length + 1)
TryToReDim = ArrayIn
End Function
In particular, at first
I thought that if I used "Of" in my function definition then my
function call would have to look like this:

TryToReDim(Of String)(xxx)
But it turns out
that if I omit the Of clause when I call the function .NET infers
the value of T from ArrayIn!

It's not .Net, silly... it's "braindead" VB...
(sorry for the oneliner, that braindead thing really got on my nerves)

Regards,

Branco.
 
Back
Top