VBA arrays and Operators - arrays

I am trying to write a couple lines of code in VBA for excel, here is what the VB.Net version of the code looks like.
ThisVariable <<= 8
Variable.Add(ThisVariable)
How do I write these lines of code in VBA? VBA does not have the "<<=" operator and does not have the .Add property of an array. Any help would be greatly appreciated.

For the first question, VBA doesn't have built in bit shifting, but you can add a function that does it for you. Try this one.
For the second question, to increase an array size, you have to use the ReDim command. Here's the info on that.
Also, you could use a collection instead of an array. With a collection, you can add & delete item within it at will.
Good luck

<<= left-shifts the number by 8 binary digits, i.e. multiplies the number by 256. For adding something to an array you can re-dim the array:
ThisVariable = ThisVariable * 256
Dim U As Long
Dim L As Long
L = LBound (Variable)
U = UBound (Variable)
ReDim Preserve Variable (L To U+1)
Variable (U+1) = ThisVariable
Note that redim'ing the array just to add an element is not very efficient. You should try to find another approach (e.g. use a larger array and store the number of "valid" elements in a counter variable -- or even create a class module for that).

Related

Excel VBA: How to concatenate variant array elements (row numbers) into a range object?

I did research this question but could not find the specific answer I was looking for and am actually even more confused at present.
I created a macro that would run through rows on a sheet and run boolean checks on a number of cells in each row that looked for the presence or absence of specific values, or calculated the outcome of a specific inequality. On the basis of those checks, the macro may or may not pass the row number into a specific array. That part is working fine.
My issue is, now that I have the row numbers (stored in variant arrays) - I cannot figure out how to properly concatenate that data into a range and then take a bulk excel action on those items. What I'd like to do is create a range of those values and then delete all of those rows at once rather than looping through.
My macro is on my work computer, but here's something I wrote that should explain what I'm trying to do:
Sub Test()
Dim Str As String
Dim r As Range
Dim i, a As Integer
Dim Count As Integer
Dim RngArray()
Count = ThisWorkbook.Sheets("Sheet1").Cells(Rows.Count, "A:A").End(xlUp).Row
ReDim RngArray(Count)
a = 0
For i = 1 To Count
If Not i = Count Then
RngArray(a) = i
Str = Str & RngArray(a) & ":" & RngArray(a) & ", "
a = a + 1
ElseIf i = Count Then
RngArray(a) = i
Str = Str & RngArray(a) & ":" & RngArray(a)
a = a + 1
Else: End If
Next i
Set r = Range(Str)'Error Can Appear here depending on my concatenation technique
Range(Str).EntireRow.Delete 'error will always appear here
End Sub
I've combined a few steps here and left out any Boolean checks; in my actual macro the values in the arrays are already stored and I loop from LBound to UBound and concatenate those values into a string of the form ("1:1, 2:2, 3:3, ...., n:n")
The reason why I did this is that the rows are all over the sheet and I wanted to get to a point where I could pass the argument
Range("1:1, 2:2, 3:3, ..., n:n").EntireRow.Delete
I think it's clear that I'm just not understanding how to pass the correct information to the range object. When I try to run this I get a "Method Range of Object Global" error message.
My short term fix is to just loop through and clear the rows and then remove all of the blank rows (the macro keeps track of absolute positions of the rows, not the rows after an iterative delete) - but I'd like to figure out HOW to do this my way and why it's not working.
I'm open to other solutions as well, but I'd like to understand what I'm doing wrong here. I should also mention that I used the Join() to try to find a workaround and still received the same type of error.
Thank you.
After some experimentation with my dataset for the macro above, I discovered that it worked on small sets of data in A:A but not larger sets.
I ran Debug.Print Len(Str) while tweaking the set size and macro and found that it appears Range() can only accept a maximum of 240 characters. I still don't understand why this is or the reason for the specific error message I received, but the macro will work if Len(Str) < 240.
I'll have to loop backwards through my array to delete these rows if I want to use my present method...or I may just try something else.
Thanks to Andrew for his attention to this!

For-looping VBA vectors

I'm trying to create a vector in VBA using a for loop. My problem is that VBA doesn't allow me to have a different equation for the first vector coordinate. When trying to run it I get "expected array" as an error message
'radius calculations
r(1) = (al * Log(al) / (al - 1)) * rb ' middle radius of block 1, trying to calculate first entry
'in r-vector, since this equation is different from the rest
r_m(1) = rb 'r_i-1/2 i=1
For i = 2 To n_r
r(i) = al * r(i - 1) ' r_i
r_m(i) = (r(i) - r(i - 1)) / (Log(r(i) / r(i - 1)))
Next i
al and rb is defined as Double (Public Const), while r is defined as a string. I have only used Matlab in the past, and only read intro guides to VBA (VBA for Dummies etc.)
Greatly appreciate all help in the matter
E
VBA doesn't treat strings the same way it treats arrays (like you'd see in other languages). r(1) looks like array syntax to VBA (as can be seen here); therefore, it's going to error out if it's declared as a string.
So, essentially, VBA is interpreting r(1) to mean, "I have an array named r and I want to store something into element number 1". But, instead, it's attempting to do this to an immutable string.
The Split function can be used to turn a string into an array, if that's what you need.
Still, it'll be best if you straight-up declare an array for your vector math.
Dim myArray() As Double
Dim myArray2(10, 10, 10) As Double
The ReDim keyword can be used to resize an array, even within a for loop. (Just don't forget to ReDim Preserve if you need to make sure that the contents don't get wiped during this operation).

Type conversion (typecasting). Convert array to matrix (C code to Visual Basic)

I am working at rewriting C code to Visual Basic now. And I got some problems with this syntaxes:
C code:
typedef uint8_t state_t[4][4];
static state_t* state;
void myFunc (uint8_t* output)
{
state = (state_t*)output;
// some actions
}
My Visual Basic code:
Dim state(3, 3) As Byte
Sub myVBFunc(ByRef output() As Byte)
state = output ' does not work: Cannot convert Byte() to Byte(*,*)'
'state = CType(output, Byte(,)) ''also does not work'
' some actions'
End Sub
I can do it in C, because array name is a pointer to the first element. And C can automatically converts this types. So VB cannot. I'm stacked here.
How to convert this array to matrix?
Or how to avoid using of this in VB?
P.S. Actually I work in Visual Basic 6.0. (try to modify old project).
If the question is about VB6, then you have three options:
a. Change everything to use dynamic arrays:
Dim state() As Byte
Sub myVBFunc(ByRef output() As Byte)
state = output
End Sub
You can set up the array to call myVBFunc by redimensioning e.g.:
Dim myarray() as Byte
ReDim myarray(3,3)
myVBFunc(myarray)
b. Embed the fixed size array in a user defined type and pass that around instead:
Type type_state
state(3, 3) As Byte
End Type
Dim state As type_state
Sub test()
Dim something As type_state
myVBFunc something
End Sub
Sub myVBFunc(ByRef output As type_state)
state = output
End Sub
c. Use objects. Set up a class to hold your fixed sized array and set up getters and setters to access it. A short answer would be to long for me to show in this post.
(You will need extra code to set up the array itself)
I used simple way: just assign it trough the loop using index multiplication.
For k = 0 To 3
For j = 0 To 3
state(k, j) = temp(4 * k + j)
Next j
Next k
Why I didn't use Guillermo Phillips's answer:
a. I can't replace state(3,3) from my question just as
Dim state() As Byte
because there is some specific math here which is really simple for matrices.
I found useful ReDim option. Especially when use it in this way:
ReDim Preserve input(15)
It saves previous values of input array or put zeros in new positions!
But we can convert array to matrix using ReDim:
ReDim cannot change the number of dimensions
b. Idea of using of user defined type is really interesting but I stall can't figure out how to assign then Byte() to my
Type type_state
state(3, 3) As Byte
End Type
c. Agree! This way is useful. I just avoid it because of it's complicity (much easier to create function with inbuilt loop). But maybe using object is more safety?..

Why does my VB.NET array have extra values?

I declare my array
Dim A(N) As Integer
When I loop from 1 To N or 0 To N-1 there's an extra value at one end or the other.
What's going on?
(Intended to be a canonical question/answer.)
In VB.NET arrays almost always* have a lower bound of 0 and are declared mentioning their upper bound, not their length.
They did change the VB.NET syntax early on to allow you to remind yourself if needed:
Dim A(0 To N) As Integer
That 0 can't be anything else (such as a 1 or a constant zero).
You can loop through all VB.NET array indexes using
For i = LBound(A) To UBound(A)
or, more simply,
For i = 0 To N
(*) You can use the .NET Framework to create arrays with other lower bounds, but you need to refer to them as Array and thus with late binding (and probably Option Strict Off).

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