What is the best way to clear an array of strings? - vb.net

What is the best way to clear an array of strings?

Wrong way:
myArray = Nothing
Only sets the variable pointing to the array to nothing, but doesn't actually clear the array. Any other variables pointing to the same array will still hold the value. Therefore it is necessary to clear out the array.
Correct Way
Array.Clear(myArray,0,myArray.Length)

And of course there's the VB way using the Erase keyword:
Dim arr() as String = {"a","b","c"}
Erase arr

Depending what you want:
Assign Nothing (null)
Assign a new (empty) array
Array.Clear
Last is likely to be slowest, but only option if you don't want a new array.

If you're needing to do things like clear, you probably want a collection like List(Of String) rather than an array.

redim arr(1,1,1,1)
and then
redim (z,x,y,v) to your dimensions

Here's a simple call that I use to clear the contents of a string array:
Public Sub ClearArray(ByRef StrArray As String())
For iK As Int16 = 0 To StrArray.Length - 1
StrArray(iK) = ""
Next
End Sub
Then just call it with:
ClearArray(myLocalArray)

If you need to reinitialize with empty strings or other values not equal to Nothing/Null, you may get further using an extension method:
Option Strict On : Option Explicit On : Option Infer On
...
Public Delegate Sub ArrayForAllDelegate(Of T)(ByRef message As T)
<Extension>
Public Function ForAll(Of T)(ByRef self As T(), f As ArrayForAllDelegate(Of T)) As T()
Dim i = 0
While i < self.Length
f(self(i))
i += 1
End While
Return self
End Function
Then your initialization code:
Dim a = New String(3 - 1) {"a", "b", "c"}
...
a.ForAll(Sub(ByRef el) el = "") 'reinitialize the array with actual empty strings

I use this code:
Redim myarray(-1)

Related

Are all arrays forced to be ByRef when passed into a subroutines? [duplicate]

I have a Sub with an array of strings as parameter:
Private Sub des(ByVal array() As String)
Dim i As Integer
For i = 0 To UBound(array)
array(i) = "hy"
Next
End Sub
When I call the function inside my main function, the value of str changes even if the array is passed to the function ByVal:
Dim str() As String
str = {"11111", "22222", "33333", "44444", "5555", "66666"}
des(str)
I tried making a copy of the array in the Sub, but it still changes in the main function.
Private Sub des(ByVal array() As String)
Dim i As Integer
Dim array2() As String
array2 = array
For i = 0 To UBound(array)
array(i) = "hy"
Next
End Sub
I read on a site that you cannot pass arrays ByVal. Is this true? If so, how should I proceed?
Try this:
Dim i As Integer
Dim array2() As String
array2 = array.Clone()
For i = 0 To UBound(array2)
array2(i) = "hy"
Next
The key difference is the .Clone(), that actually makes a shallow copy of array, and changing the values in array2 will no longer affect your str value in the main code.
Arrays are reference types. That means that when you pass an Array to your function, what is passed is always a reference, not a copy of the array. The Array in your function refers to the same array object as the Array in your calling code.
The same thing happens when you do the assign (it is not a copy!) in your second example: all you've done is make yet another reference to the same object. That is why Boeckm's solution works -- the Clone() call does make a new array and assign it values which are copies of the values in the original array.
In Visual Basic .NET, regarding arrays as parameters, there are two important rules you have to be aware of:
Arrays themselves can be passed as ByVal and ByRef.
Arrays' elements can always be modified from the function or subroutine.
You already know that you can modify the elements of an array inside a subprocess (subroutine or function), no matter how that array is defined as parameter inside that subprocess.
So, given these two subroutines:
Private Sub desval(ByVal array() As String)
array = {}
End Sub
Private Sub desref(ByRef array() As String)
array = {}
End Sub
And this very simple auxiliary subroutine (here I'll use the Console):
Private Sub printArr(ByVal array() As String)
For Each str In array
Console.WriteLine(str)
Next
End Sub
you can do these simple tests:
Dim arr1() as String = {"abc", "xyz", "asdf"}
Console.WriteLine("Original array:")
printArr(arr1)
Console.WriteLine()
Console.WriteLine("After desval:")
desval(arr1)
printArr(arr1)
Console.WriteLine()
Console.WriteLine("After desref:")
desref(arr1)
printArr(arr1)
I read on a site that you cannot pass arrays ByVal. Is this true?
No, that is not true.
An array in the .NET framework is a reference type. When you create an array, an object of System.Array will be created and its reference is assigned to the reference variable.
When you call a des method, the reference of the array object will be passed. In des method, ByVal parameter is a reference parameter variable of type System.Array, and it receive a copy of reference of an array object.
MSDN article - Passing Arguments by Value and by Reference (Visual Basic)

For Each Value to array vb.net

I need to extract a value from a process array to myArray:
File: MyClass.vb
Public Function getTheRunningProcess()
Dim MyArray As Array
For Each prso In Process.GetProcesses(System.Environment.MachineName)
'This is the value i want to extract and put it in a Array
MyArray() = prso.ProcessName.ToString
Next
'Now i need to return the MyArray so i can access its value from anywhere
Return MyArray
End Function
So i can access this value from anywhere like so:
File: Form1.vb
For Each MyValue As TheValueFromFunction_getTheRunningProcess()
Listbox.item.addItem(MyValue)
Next
Now you might be thinking i can directly use the process in the File: Form1.vb but here is the situation, i have to use this function over and over again in different form as the application demands it has to be.
So if this is possible then how?
Or if there is any other way to do this?
Please show me the right path.
Thanks in advance :)
Public Function getTheRunningProcess() As Generic.List(Of String)
Dim processList As New Generic.List(Of String)
For Each prso In Process.GetProcesses(System.Environment.MachineName)
processList.Add(prso.ProcessName.ToString)
Next
Return processList
End Function
You could try doing it with Lists... And then
Dim myProccessList = getTheRunningProcess()
For Each p In myProccessList
'Do something with p
Next
Hope it helps

Sub / Function array parameter altered

I have a Sub with an array of strings as parameter:
Private Sub des(ByVal array() As String)
Dim i As Integer
For i = 0 To UBound(array)
array(i) = "hy"
Next
End Sub
When I call the function inside my main function, the value of str changes even if the array is passed to the function ByVal:
Dim str() As String
str = {"11111", "22222", "33333", "44444", "5555", "66666"}
des(str)
I tried making a copy of the array in the Sub, but it still changes in the main function.
Private Sub des(ByVal array() As String)
Dim i As Integer
Dim array2() As String
array2 = array
For i = 0 To UBound(array)
array(i) = "hy"
Next
End Sub
I read on a site that you cannot pass arrays ByVal. Is this true? If so, how should I proceed?
Try this:
Dim i As Integer
Dim array2() As String
array2 = array.Clone()
For i = 0 To UBound(array2)
array2(i) = "hy"
Next
The key difference is the .Clone(), that actually makes a shallow copy of array, and changing the values in array2 will no longer affect your str value in the main code.
Arrays are reference types. That means that when you pass an Array to your function, what is passed is always a reference, not a copy of the array. The Array in your function refers to the same array object as the Array in your calling code.
The same thing happens when you do the assign (it is not a copy!) in your second example: all you've done is make yet another reference to the same object. That is why Boeckm's solution works -- the Clone() call does make a new array and assign it values which are copies of the values in the original array.
In Visual Basic .NET, regarding arrays as parameters, there are two important rules you have to be aware of:
Arrays themselves can be passed as ByVal and ByRef.
Arrays' elements can always be modified from the function or subroutine.
You already know that you can modify the elements of an array inside a subprocess (subroutine or function), no matter how that array is defined as parameter inside that subprocess.
So, given these two subroutines:
Private Sub desval(ByVal array() As String)
array = {}
End Sub
Private Sub desref(ByRef array() As String)
array = {}
End Sub
And this very simple auxiliary subroutine (here I'll use the Console):
Private Sub printArr(ByVal array() As String)
For Each str In array
Console.WriteLine(str)
Next
End Sub
you can do these simple tests:
Dim arr1() as String = {"abc", "xyz", "asdf"}
Console.WriteLine("Original array:")
printArr(arr1)
Console.WriteLine()
Console.WriteLine("After desval:")
desval(arr1)
printArr(arr1)
Console.WriteLine()
Console.WriteLine("After desref:")
desref(arr1)
printArr(arr1)
I read on a site that you cannot pass arrays ByVal. Is this true?
No, that is not true.
An array in the .NET framework is a reference type. When you create an array, an object of System.Array will be created and its reference is assigned to the reference variable.
When you call a des method, the reference of the array object will be passed. In des method, ByVal parameter is a reference parameter variable of type System.Array, and it receive a copy of reference of an array object.
MSDN article - Passing Arguments by Value and by Reference (Visual Basic)

How can I delete an item from an array in VB.NET?

How can I delete an item from an array in VB.NET?
As Heinzi said, an array has a fixed size. In order to 'remove an item' or 'resize' it, you'll have to create a new array with the desired size and copy the items you need as appropriate.
Here's code to remove an item from an array:
<System.Runtime.CompilerServices.Extension()> _
Function RemoveAt(Of T)(ByVal arr As T(), ByVal index As Integer) As T()
Dim uBound = arr.GetUpperBound(0)
Dim lBound = arr.GetLowerBound(0)
Dim arrLen = uBound - lBound
If index < lBound OrElse index > uBound Then
Throw New ArgumentOutOfRangeException( _
String.Format("Index must be from {0} to {1}.", lBound, uBound))
Else
'create an array 1 element less than the input array
Dim outArr(arrLen - 1) As T
'copy the first part of the input array
Array.Copy(arr, 0, outArr, 0, index)
'then copy the second part of the input array
Array.Copy(arr, index + 1, outArr, index, uBound - index)
Return outArr
End If
End Function
You can use it as such:
Module Module1
Sub Main()
Dim arr = New String() {"abc", "mno", "xyz"}
arr.RemoveAt(1)
End Sub
End Module
The code above removes the second element ("mno") [which has an index of 1] from the array.
You need to be developing in .NET 3.5 or higher in order to use the extension method.
If you're using .NET 2.0 or 3.0, you can call the method as such
arr = RemoveAt(arr, 1)
I hope this is what you need.
Update
After running tests based on ToolMakerSteve's comment it appears the initial code does not modify the array you want to update because of the ByVal used in the function's declaration. However, writing code like arr = arr.RemoveAt(1) or arr = RemoveAt(arr, 1) does modify the array because it reassigns the modified array to the original.
Find below the updated method (subroutine) for removing an element from an array.
<System.Runtime.CompilerServices.Extension()> _
Public Sub RemoveAt(Of T)(ByRef arr As T(), ByVal index As Integer)
Dim uBound = arr.GetUpperBound(0)
Dim lBound = arr.GetLowerBound(0)
Dim arrLen = uBound - lBound
If index < lBound OrElse index > uBound Then
Throw New ArgumentOutOfRangeException( _
String.Format("Index must be from {0} to {1}.", lBound, uBound))
Else
'create an array 1 element less than the input array
Dim outArr(arrLen - 1) As T
'copy the first part of the input array
Array.Copy(arr, 0, outArr, 0, index)
'then copy the second part of the input array
Array.Copy(arr, index + 1, outArr, index, uBound - index)
arr = outArr
End If
End Sub
Usage of the method is similar to the original, except there is no return value this time so trying to assign an array from the return value will not work because nothing is returned.
Dim arr = New String() {"abc", "mno", "xyz"}
arr.RemoveAt(1) ' Output: {"abc", "mno"} (works on .NET 3.5 and higher)
RemoveAt(arr, 1) ' Output: {"abc", "mno"} (works on all versions of .NET fx)
arr = arr.RemoveAt(1) 'will not work; no return value
arr = RemoveAt(arr, 1) 'will not work; no return value
Note:
I use a temporary array for the process because it makes my intentions clear and that is exactly what VB.NET does behind the scenes when you do Redim Preserve. If you would like to modify the array in-place using Redim Preserve, see ToolmakerSteve's answer.
The RemoveAt methods written here are extension methods. In order for them to work, you will have to paste them in a Module. Extension methods will not work in VB.NET if they are placed in a Class.
Important If you will be modifying your array with lots of 'removes', it is highly recommended to use a different data structure such as List(Of T) as suggested by other answerers to this question.
You can't. I would suggest that you put the array elements into a List, at least then you can remove items. An array can be extended, for example using ReDim but you cannot remove array elements once they have been created. You would have to rebuild the array from scratch to do that.
If you can avoid it, don't use arrays here, use a List.
One line using LINQ:
Dim arr() As String = {"uno", "dos", "tres", "cuatro", "cinco"}
Dim indx As Integer = 2
arr = arr.Where(Function(item, index) index <> indx).ToArray 'arr = {"uno", "dos", "cuatro", "cinco"}
Remove first element:
arr = arr.Skip(1).ToArray
Remove last element:
arr = arr.Take(arr.length - 1).ToArray
That depends on what you mean by delete. An array has a fixed size, so deleting doesn't really make sense.
If you want to remove element i, one option would be to move all elements j > i one position to the left (a[j - 1] = a[j] for all j, or using Array.Copy) and then resize the array using ReDim Preserve.
So, unless you are forced to use an array by some external constraint, consider using a data structure more suitable for adding and removing items. List<T>, for example, also uses an array internally but takes care of all the resizing issues itself: For removing items, it uses the algorithm mentioned above (without the ReDim), which is why List<T>.RemoveAt is an O(n) operation.
There's a whole lot of different collection classes in the System.Collections.Generic namespace, optimized for different use cases. If removing items frequently is a requirement, there are lots of better options than an array (or even List<T>).
Yes, you can delete an element from an array. Here is an extension method that moves the elements as needed, then resizes the array one shorter:
' Remove element at index "index". Result is one element shorter.
' Similar to List.RemoveAt, but for arrays.
<System.Runtime.CompilerServices.Extension()> _
Public Sub RemoveAt(Of T)(ByRef a() As T, ByVal index As Integer)
' Move elements after "index" down 1 position.
Array.Copy(a, index + 1, a, index, UBound(a) - index)
' Shorten by 1 element.
ReDim Preserve a(UBound(a) - 1)
End Sub
Usage examples (assuming array starting with index 0):
Dim a() As String = {"Albert", "Betty", "Carlos", "David"}
a.RemoveAt(0) ' Remove first element => {"Betty", "Carlos", "David"}
a.RemoveAt(1) ' Remove second element => {"Betty", "David"}
a.RemoveAt(UBound(a)) ' Remove last element => {"Betty"}
Removing First or Last element is common, so here are convenience routines for doing so (I like code that expresses my intent more readably):
<System.Runtime.CompilerServices.Extension()> _
Public Sub DropFirstElement(Of T)(ByRef a() As T)
a.RemoveAt(0)
End Sub
<System.Runtime.CompilerServices.Extension()> _
Public Sub DropLastElement(Of T)(ByRef a() As T)
a.RemoveAt(UBound(a))
End Sub
Usage:
a.DropFirstElement()
a.DropLastElement()
And as Heinzi said, if you find yourself doing this, instead use List(Of T), if possible. List already has "RemoveAt" subroutine, and other routines useful for inserting/deleting elements.
My favorite way:
Imports System.Runtime.CompilerServices
<Extension()> _
Public Sub RemoveAll(Of T)(ByRef arr As T(), matching As Predicate(Of T))
If Not IsNothing(arr) Then
If arr.Count > 0 Then
Dim ls As List(Of T) = arr.ToList
ls.RemoveAll(matching)
arr = ls.ToArray
End If
End If
End Sub
Then in the code, whenever I need to remove something from an array I can do it by some property in some object in that array having a certain value, like:
arr.RemoveAll(Function(c) c.MasterContactID.Equals(customer.MasterContactID))
Or if I already know the exact object I want to remove, I can just do:
arr.RemoveAll(function(c) c.equals(customer))
The variable i represents the index of the element you want to delete:
System.Array.Clear(ArrayName, i, 1)
This may be a lazy man's solution, but can't you just delete the contents of the index you want removed by reassigning their values to 0 or "" and then ignore/skip these empty array elements instead of recreating and copying arrays on and off?
Public Sub ArrayDelAt(ByRef x As Array, ByVal stack As Integer)
For i = 0 To x.Length - 2
If i >= stack Then
x(i) = x(i + 1)
x(x.Length-1) = Nothing
End If
Next
End Sub
try this
Seems like this sounds more complicated than it is...
Dim myArray As String() = TextBox1.Lines
'First we count how many null elements there are...
Dim Counter As Integer = 0
For x = 0 To myArray.Count - 1
If Len(myArray(x)) < 1 Then
Counter += 1
End If
Next
'Then we dimension an array to be the size of the last array
'minus the amount of nulls found...
Dim tempArr(myArray.Count - Counter) As String
'Indexing starts at zero, so let's set the stage for that...
Counter = -1
For x = 0 To myArray.Count - 1
'Set the conditions for the new array as in
'It .contains("word"), has no value, length is less than 1, ect.
If Len(myArray(x)) > 1 Then
Counter += 1
'So if a value is present, we move that value over to
'the new array.
tempArr(Counter) = myArray(x)
End If
Next
Now you can assign tempArr back to the original or what ever you need done with it as in...
TextBox1.Lines = tempArr (You now have a textbox void of blank lines)
If the array is a string array you are able to then do the following:
AlphaSplit = "a\b\c".Split("\")
MaxIndex = AlphaSplit.GetUpperBound(0)
AlphaSplit = AlphaSplit.Where(Function(item, index) index <> MaxIndex).ToArray
AlphaJoin = String.Join("\", PublishRouteSplit)
How about this method:
Get a method which return an array, say tempArray
tempArray is supposed to have at least 1 less element to your array, say permArray
The method should take and integer param (this will be the index of the unwanted element) say ommitIndex and your permArray
In the method, copy all elements excluding the element as position ommitIndex from permArray to tempArray
The method returns tempArray so update permArray with the method.
Here's a snippet
Function updateArray(ommitIndex As Integer, array() As String) As Array
Dim tempArray(array.Length - 2) As String
Dim counter As Integer = 0
For i As Integer = 0 To (array.Length - 1)
If (i <> ommitIndex) Then
tempArray(counter) = array(i)
counter += 1
End If
Next
Return tempArray
End Function

Is it possible to do a For...Each Loop Backwards?

I don't believe this is possible by conventional methods, but something like this verbose code:
For Each s As String In myStringList Step -1
//' Do stuff here
Next
I will probably have to invert the myString object before a conventional For..Each Loop, correct?
I think the documentation referenced in Mike's answer below is extremely misleading. The order of For Each is defined by the collection it's called (i.e. its implementation of IEnumerable/IEnumerable<T>), but that's not the same as saying it shouldn't be used when the order is important. Many collections (such as arrays, List<T> etc) always go in the "natural" order.
Part of the documentation does allude to this:
Traversal Order. When you execute a
For Each...Next loop, traversal of the
collection is under the control of the
enumerator object returned by the
GetEnumerator method. The order of
traversal is not determined by Visual
Basic, but rather by the MoveNext
method of the enumerator object. This
means that you might not be able to
predict which element of the
collection is the first to be returned
in element, or which is the next to be
returned after a given element.
That's not at all the same as saying it can't be relied upon - it can be relied upon if you know that the collection you're iterating over will produce the results in the desired order. It's not like it's going to pick elements at random. The behaviour in terms of IEnumerable/IEnumerable<T> is clearly defined on that page.
The most important exceptions to predictable orderings are dictionaries and sets, which are naturally unordered.
To reverse an IEnumerable<T>, use Enumerable.Reverse - but if you need to iterate in reverse over a collection which is indexed by position (such as an array or List<T>) then it would be more efficient to use a For loop starting at the end and working backwards.
You'd have to do something like:
For i as integer = myStringList.Count-1 to 0 step -1
dim s as string = myStringList.Item(i)
' Do your stuff with s
Next i
But as far as I know, you can't do a "For...Each" backwards, though that would be nice in a few instances.
Sadly, the MSDN docs on For Each state that the For Each construct is there explicitly for cases where the order of the iteration is unimportant (unordered sets and the like). So there is unfortunately no concept of reversing a For Each, as the order is not defined anyway.
Good luck!
Call the System.Linq.Enumerable.Reverse method to get an IEnuemrable(Of TSource) in the reverse order of your enumerable source.
Testig in Framework 4, the code
For Each s As String In myStringList.Reverse
//' Do stuff here
Next
it wasn't working, the right way to do it is:
myStringList.Reverse() ' it is a method not a function
For Each s As String In myStringList
//' Do stuff here
Next
Look at: MSDN: Reserve
For Each s As String In myStringList.Reverse
//' Do stuff here
Next
The reason it can't be done is that, as a basic design feature, For Each can iterate over an enumerable without a last element, or with an as yet unknown last element.
This works:
Dim myArray() As String = {"1", "2", "3", "4", "5"}
For Each n As String In myArray.Reverse
Debug.Print(n)
Next
Output: 5 4 3 2 1
'
This also works:
Dim myArray() As String = {"1", "2", "3", "4", "5"}
For i As Integer = myArray.Length - 1 To 0 Step -1
Debug.Print(myArray(i))
Next
Output: 5 4 3 2 1
What you have to do is create an array with your for each you had before, then use array.reverse and run the for each on the array. Done
Cheers
Depending on what happens inside your loop you can do an .InsertAt(object,0) instead of an .Add and produce the same result as a reverse enumeration.
You can add a extended function to the class you are trying to reverse
<Serializable()> Public Class SomeCollection
Inherits CollectionBase
Public Sub New()
End Sub
Public Sub Add(ByVal Value As Something)
Me.List.Add(Value)
End Sub
Public Sub Remove(ByVal Value As Something)
Me.List.Remove(Value)
End Sub
Public Function Contains(ByVal Value As Something) As Boolean
Return Me.List.Contains(Value)
End Function
Public Function Item(ByVal Index As Integer) As Something
Return DirectCast(Me.List.Item(Index), Something)
End Function
Public Function Reverse() As SomeCollection
Dim revList As SomeCollection = New SomeCollection()
For index As Integer = (Me.List.Count - 1) To 0 Step -1
revList.List.Add(Me.List.Item(index))
Next
Return revList
End Function
End Class
Then you would call it like this
For Each s As Something In SomeCollection.Reverse
Next
first you should create a list(of string) in for each statement, and after that make another normal for .. step -1 ..next statement. see an example:
Dim ff As New List(Of Integer)
For Each rRow As DataRow In DeletedRows
Dim item As Integer
item = rRow.Table.Rows.IndexOf(rRow)
ff.Add(item)
Next
For i As Integer = ff.Count - 1 To 0 Step -1
dim item as integer=ff(i)
next i
i hope that be helpful
The accepted answer explains why, but to add another example, for a collection with key (SortedList, SortedDictionary, Dictionary, etc.) you can do
For Each Id as Tkey in MyCollection.Keys.Reverse
// The item in question is now available as MyCollection(Id)
Next
' create a list of whatever and step through For Loop Collecting
dim revList as New List (of ToolStripItem)
For each objItem as ToolStripItem in Menu.DropDownItems
revList.Add(objItem)
next
revList.reverse ' reverse the order of that list
' step through the reverse ordered list
for each objItem as ToolStripItem in revList
' do whatever (like removing your menu from an ArrayMenu)
next
' replace ToolStripItem and Menu.DropDownItems with whatever you need