VB.net How to access row in 2 dimensional array? - arrays

I want to read all elements of the first row from a 2-D array in VB.NET. Is there a simple way to do it rather than running a loop. I know that in MATLAB there is possibilty to say: A(1,:) and its done.
Thank you.

There is nothing built into the array itself to read without a loop but you could use a LINQ query, e.g.
Dim firstRow = Enumerable.Range(0, myArray.GetUpperBound(1) + 1).
Select(Function(i) myArray(0, i))
Note that what is a "row" and what is a "column" is not defined for a 2D array so it's really up to you whether you want to use that or this:
Dim firstRow = Enumerable.Range(0, myArray.GetUpperBound(0) + 1).
Select(Function(i) myArray(i, 0))
That gives you an enumerable list of the specified elements so the question is, what do you want to do with it? You haven't actually specified. As it is, all you can really do is enumerate it, which means running a For Each loop over it. That kinda defeats the purpose of avoiding a loop in the first place. If you're saying, without actually saying, that you want those elements in a 1D array then you can call ToArray:
Dim firstRow = Enumerable.Range(0, myArray.GetUpperBound(1) + 1).
Select(Function(i) myArray(0, i)).
ToArray()
EDIT:
Here is an extension method that you could use to do the job:
Imports System.Runtime.CompilerServices
Public Module ArrayExtensions
<Extension>
Public Function GetRow(Of T)(source As T(,), rowIndex As Integer) As T()
Return Enumerable.Range(0, source.GetUpperBound(1) + 1).
Select(Function(i) source(rowIndex, i)).
ToArray()
End Function
End Module
It then makes the calling code much cleaner, especially if you need to do it multiple times, e.g.
Dim firstRow = myArray.GetRow(0)

If you don't know size of your array u can use nested loop For Each like this:
For each row in array
For each col in row
''something here like Debug.Write(col.ToString)
Next
Next
If you know your size and you want specific item in the array you simply do that like in any other loop "for" like this:
For index As Integer = 5 To 5
For index2 As Integer = 3 To 3
Debug.Write(index2.ToString)
Next
Next

Related

Excel transpose 2D array - Out of memory issue

I have a VBA macro, where I want to write out an array to an Excel sheet.
I'm getting an "Out of memory" runtime error on some machines. I can run it easily on my development PC, but my client has issues with it.
Here I define my Values array:
Dim Values()
Dim idx As Long
idx = 0
Then I have a for cycle where I dynamically redim the array, and add my values to it:
for cycle...
ReDim Preserve Values(16, 0 To idx)
Values(0, idx) = "some text"
Values(1, idx) = "some other text"
....
Values(15, idx) = "last values for this row"
idx = idx + 1
next
Then here is where my code fails:
With ws
.Range(.Cells(1, 1), .Cells(1+ idx - 1, 16)).value = TransP(Values)
End With
Here's the TransP transposing function:
Public Function TransP(var As Variant) As Variant
Dim outP() As Variant, i As Long, j As Long
ReDim outP(LBound(var, 2) To UBound(var, 2), LBound(var, 1) To UBound(var, 1))
For i = LBound(outP) To UBound(outP)
For j = LBound(var) To UBound(var)
outP(i, j) = var(j, i)
Next
Next
TransP = outP
End Function
As I said, I can run the macro, and get something like 108770 rows. The same 108770 rows don't work on my clients PC.
I expect that the TransP function gives up on his PC, so should I split up the array into multiple smaller chunks, and write them 1 by 1?
Or my data model is not good?
You could also create a loop to write your output array row by row, it will take more time but you will most likely don't get out of memory error.
In the past when I've got out of memory issues with an arrays I just tried to perform actions using regular excel commands, in this case you could just copy range and than paste transposed values:
.PasteSpecial Paste:=xlPasteValues, Transpose:=True
Your method of appending elements to Values is inefficient because every time an element is added a new array is created and the values of the existing one copied to it. During this time twice the memory is in use, and if a large array is copied in this way multiple times in quick succession only heaven can know what demands are put on the RAM management.
The better way is to dimension the array larger than will be required (once), count the number of elements written to it and use Redim Preserve to reduce its size (once) when you are done.
I suspect this might be due to you not specifying the dimension in your TransP function. You are also defining your loops with both arrays. You've sized them identically (although with the dimensions switched) - Just use the same one to define your For loop
Public Function TransP(var As Variant) As Variant
Dim outP() As Variant, i As Long, j As Long
ReDim outP(LBound(var, 2) To UBound(var, 2), LBound(var, 1) To UBound(var, 1))
For i = LBound(outP, 1) To UBound(outP, 1)
For j = LBound(outP, 2) To UBound(outP, 2)
outP(i, j) = var(j, i)
Next
Next
TransP = outP
End Function
I don't think it's the TransP function, since that is already handling everything in a loop. I experienced the same sort of issue and there this error occured when I tried to transfer a large multidimensional array to a range.
My solution was to create a loop and do about a 1000 rows each time, but that depends on the clients pc I guess with how many rows you can do.
To take into account that var will not end on a certain step size, you could do a while loop:
i = 0
with ws
Do while i < Ubound(var)
max = application.worksheetfunction.max(1000,Ubound(var) - i) 'At some point, you could have less than 1000 left
.range(.cells(i+1,1), .cells(i+max)) = TransP(var,i,max)
i = i + 1000
Loop
In TransP you now use the lower- and upperbound of var, but if you add two variables from the loop, you can use them to only take a piece of the array.

Combining items within an array in VB.NET

I'm very new to VB.net and I have an array, like so,
points = [[1,2],[5,6],[7,8],[9,10]]
The result I want is to be able to break this up into pairs, starting with the first item and then combining it with the next item, until the last item is paired with the previous item, like so:
new points = [[[1,2],[5,6]],[[5,6],[7,8]],[[7,8],[9,10]]]
Doing it without a loop is preferred. Being a python guy, i would do this using a list comprehension with array slicing, I'm interested to see how it would look like in VB.
thanks for your help.
One option would be:
Dim pointPairs = Enumerable.Range(0, points.Length - 2).Select(Function(n) Tuple.Create(points(n), points(n + 1)))
That will give you an IEnumerable(Of Tuple(Of Point, Point)). You could then enumerate that directly or call ToArray or ToList, e.g.
Dim pointPairs = Enumerable.Range(0, points.Length - 2).
Select(Function(n) Tuple.Create(points(n), points(n + 1))).
ToArray()

ReDim existing array with a second dimension?

I declared an array in my VBA function that has a dynamic size. As I cannot ReDim the first dimension of a two- or more-dimensional array, can I add a second dimension to a set array?
This is how I dynamically set the size of my array.
Dim nameArray() As String
Dim arrayCount As Long
For i = 1 To 100
ReDim Preserve nameArray(1 to arrayCount)
nameArray(arrayCount) = "Hello World"
arrayCount = arrayCount + 1
Next i
Now I would like to add a second dimension.
ReDim Preserve nameArray(1 To arrayCount, 1 To 5)
doesn't work.
Is there a workaround?
There isn't any built-in way to do this. Just create a new two-dimensional array and transfer the contents of your existing one-dimensional array into the first row of that new array.
This is what this function does:
Function AddDimension(arr() As String, newLBound As Long, NewUBound As Long) As String()
Dim i As Long
Dim arrOut() As String
ReDim arrOut(LBound(arr) To UBound(arr), newLBound To NewUBound)
For i = LBound(arr) To UBound(arr)
arrOut(i, newLBound) = arr(i)
Next i
AddDimension = arrOut
End Function
Example usage:
nameArray = AddDimension(nameArray, 1, 5)
There is one (also works to delete a dimension), but you will have to think in terms of worksheet dimensions...
use transposition
While I highly prefer the previous 'by hand' method from Jean-François Corbett's and I don't like to rely on Excel build-in function (especially this one!), I would just like to clarify another way for future readers coming here:
adding a dimension to a 1d line vector (a row) means transposing it in Excel
Here, nameArray(1 to arrayCount) is a row (index is a column number) and because of it, if you add a dimension it will become a column since 2d arrays are indexed as (row,column). So, you can just do this:
nameArray = Application.Worksheetfunction.Transpose(nameArray) 'transforms the array to nameArray(1 To arrayCount, 1 To 1), so then:
redim preserve nameArray(1 To arrayCount, 1 To 5)
without any other manipulation.
BUT beware of the very confusing Excel's Transpose function (at least it is the case for me!): the advantage here is that it automatically adds a dimension and redimensions the array for you.
It works as expected only because you are using a 'based 1' index '1d array'.
IF this is not the case, all indices will be shifted by 1, (that's how Transpose is build in to be coherent with cells and ranges). That is: if you start with
nameArray(0 to arrayCount)
you will end up with
nameArray(1 to arrayCount + 1, 1 to 5)
with precautions
While I am at it, it may be off-topic and their are many topics about it, but their are other traps in this Transpose function one should never be enough warned about (not to mention it can consume more time and resources than Jean-François Corbett's solution) :
• if you are dealing with '1d column' in an excel 2d array (a column), that is an array:
nameArray(1 to arrayCount, 1 to 1) [*]
and if you make the transpose of it, the column dimension will be "skipped" and the result will be:
nameArray(1 to arrayCount)
It makes sense since you will end up with an Excel row (so why bother with an extra dim?). But I have to say this is not the intuitive behaviour I would expect, which should be more something like nameArray(1 to 1, 1 to arrayCount).
Note that, a contrario, it can be used to delete a dimension and redimension the array automatically from 2d to 1d: redim preserve the last dimension to 1 to 1 and then transpose! This is the resulting array just above.
• But finally all is not lost: suppose you transpose this array of 1 line:
nameArray(0 to 0, 0 to arrayCount)
you correctly get an array of 1 column:
nameArray(1 to arrayCount + 1, 1 to 1)
(well, almost) - and look back at ref [*] now...
Thus, if you are to use this build-in function and if, moreover you need more dimensions or worst, need to compose transpositions, it can become a bit tricky...
For all this or if you simply need to know correct indexing and number of dimensions of your arrays (not only number of columns), I would suggest a very useful function from users John Coleman and Vegard, the post just below.
While all this should appear logical and trivial for people used to work with Excel sheets, it's very not the case when you are more used to matrix manipulations and I think suggesting the use of this Transpose function should come with some precisions.

perform operations on columns or row of VBA array

In VBA is it possible to loop through, for example, every column of a 2 dimensional array and perform and operation on each column? I would like to pass each column or row of an array sequentially to a function.
Thanks.
Normally I'd not answer this because you haven't shown any effort in trying.. But I've had my own difficulty with this (with my own question currently posted looking for a better solution) and I'm bored so:
if you have a 2D array named myArr like this:
1,2,3,4,5
6,7,8,9,10
11,12,13,14,15
defined by: dim myArr(2,4) as variant
then you could loop through it with a double loop like this:
For i = LBound(myArr) To UBound(myArr)
For j = LBound(myArr, 2) To UBound(myArr, 2)
temp(j) = myArr(i, j)
Next
Next
where temp is defined and reDim'd like this:
dim temp() as variant
redim temp(lbound(myArr,2), ubound(myArr,2)) 'note the ",2" gets the bounds of the 2nd dimention
and then pass temp to a function: FunctionName(Temp)
So put that together by sticking thue function call after the inner loop and it would loop through and pass temp to the function for each row

How to iterate an array of a dynamic size in a loop

I am using VB.NET.
I would like have an array called A1 and I will perform for loop inside that array.
In the middle of the for loop, I need to remove an item from that A1 array.
I am aware that if I remove that object from A1 array, the program will crash (out of bounds error message).
Which array variable in VB.NET would allow me to perform the task above?
Code sample is most welcomed!
Thank you.
The easiest way would be to use a list instead of an array (or convert the array you have to a List(of T).
The trick then is to move from the end of the list to the front instead of the other way around.
For example:
Sub Main()
Dim RndGenerator as New Random
Dim a As New List(Of Double)
For i = 0 To 99
a.Add(RndGenerator.NextDouble() * 10) 'Populate the list
Next
For i = a.Count - 1 To 0 Step -1 'This loop performs the deletion.
If a(i) > 5 Then a.RemoveAt(i)
Next
Console.ReadKey() 'Or debugger.Break to look at the result
End Sub
This will populate a list with random numbers from 0 to 10. It then removes all numbers >5 from the list.
Edit:
Good point from Steven Doggart on not using VB6 Relics. Edited the code to use the System.Random class instead.

Resources