How to compare entire multidimensional arrays? - WinForms VB.NET - arrays

How can I compare two entire multidimensional arrays in WinForms VB.NET?
My code is checking to see whether some Subs will change the content of an array.
In order to do this, it makes a carbon copy of the original array before the subs have been executed. This carbon copy is left untouched until after the sub has happened. Once it's done the Sub, I want to see whether anything in the main array has changed. Here's my current code:
If possible = "not possible" Then 'If grid is full
'Check whether something can be done at all
For x = 0 To 5
For y = 0 To 5
copyarray(x, y) = bigarray(x, y)
Next
Next
Dim movementarray() As String = {"up", "down", "left", "right"}
For i = 0 To 3
direction = movementarray(i)
moveblocks()
Next
If copyarray = bigarray Then
'This throws an error
End If
End If
N.B. copyarray is the carbon copy of bigarray; moveblocks() is the Sub which I want to see whether it changes anything; and possible is simply a prerequisite to this code being run.
How do I do this? I ask because Visual Basic throws the following error in respect to the last If statement:
Error 1: Operator '=' is not defined for types '2-dimensional array of Integer' and '2-dimensional array of Integer'. Use 'Is' operator to compare two reference types. My Game\Form1.vb Line 282 Character 16 My Program

Ok, I solved it. Here's my solution:
Private Function whatever() as Boolean
If possible = "not possible" Then 'If grid is full
'Check whether something can be done at all
For x = 0 To 5
For y = 0 To 5
copyarray(x, y) = bigarray(x, y)
Next
Next
Dim movementarray() As String = {"up", "down", "left", "right"}
For i = 0 To 3
direction = movementarray(i)
moveblocks()
Next
Dim changed as Boolean = False
For i = 0 to 5
For j = 0 to 5
If Not copyarray(i, j) = bigarray(i, j) Then Return True : Exit Function
Next
Next
Return False
End If
Return False
End Function

Related

Exiting Nested For loop VBA and re-looping

I'm trying to work with a nested For loop. I essentially have 2 arrays and I want to use the 1st variable in array1 with the 1st variable in array2 to do some operation, and so on until the array is exhausted. Unfortunately, the Exit For, doesn't exit for the For levels. So I've tried to use a goTo command, however then I get an error of "This array is fixed or temporarily locked" clearly because I'm trying to re-access the array. I'm stuck how to get around this in VBA. Below is my code where at MsgBox some operation (that will need the pairs (dAFL,AFL),(dSF,SF), etc) will take place:
For Each vN In Array(dAFLcell, dSFcell, dSOcell, dFIGcell, dIBAcell, dIBXcell)
a = 0
For Each vN2 In Array(AFLcell, SFcell, SOcell, FIGcell, IBAcell, IBXcell)
If i = a Then
MsgBox a
GoTo end_of_for
End If
a = a + 1
Next vN2
end_of_for:
i = i + 1
Next vN
You could use a boolean flag - I don't know that it's the accepted method, but I use it from time to time.
Dim skipBool as Boolean = False
For Each vN In Array(dAFLcell, dSFcell, dSOcell, dFIGcell, dIBAcell, dIBXcell)
a = 0 'I think you want this out here, otherwise a will always equal 0
For Each vN2 In Array(AFLcell, SFcell, SOcell, FIGcell, IBAcell, IBXcell)
If Not skipBool Then 'run this stuff only if we don't want to skip it (duh!)
If i = a Then
MsgBox a
skipBool = True 'set skipBool to be True (we want to skip it!)
End If
a = a + 1
End If
Next vN2
i = i + 1
skipBool = False 'reset skipBool for the next go around
Next vN
I'm sure this code can be optimized a bit further (and to be honest, I haven't tested it), but it looks like this is what you're going for.
To be honest, the only problem might be that a = 0 was inside the second for loop, and that's why you weren't getting the results you expected. It's been a while since I've used VBA (I've only been using VB.NET), so I don't remember the exact syntax there. I'd try fixing that, and going back to the exit for method. If it still doesn't work, my code should.
Here's another possible approach:
Dim vn, Vn2 As Variant
Dim i, min As Integer
vn = Array(dAFLcell, dSFcell, dSOcell, dFIGcell, dIBAcell, dIBXcell)
Vn2 = Array(AFLcell, SFcell, SOcell, FIGcell, IBAcell, IBXcell)
If UBound(vn) <= UBound(Vn2) Then
min = UBound(vn)
Else
min = UBound(Vn2)
End If
For i = LBound(vn) To min
If vn(i) = Vn2(i) Then
MsgBox vn(i)
Exit For
End If
Next i

Distinguishing between 'Null' and '0' when taking the sum of a range of columns

I am trying to distinguish between no input or "Null" and the input of something including the number 0.
I wrote a public function called "ZeroToAppear" that works well enough when used with Index Match Functions by returning the number 0 as a string, but it will not work along with a sum function which is common in financial budgets:
Public Function ZeroToAppear(x As Variant) As Variant
If IsNull(x) Then
ZeroToAppear = Null
ElseIf x = 0 Then
ZeroToAppear = CStr(x)
Else
ZeroToAppear = x
End If
End Function
I have rationalized that the problem is that excel automatically considers null as a 0 in order to avoid ArgumentNullExceptions.
So I am trying to write another Macro that will work when taking the sum of a range that can distinguish between no input and 0 or greater input since the sum of cells with no input automatically equals zero in excel and I would like it to report null or even better report false in order to not do the sum at all.
I have started writing a function that tests each cell in the range that I would be summing to see whether it is null, error, or something. If it is null or error, I want it to report null into a test array. If there is some other input I want it to report whatever that input is into the test array. Then I want to identify if the entire test array is reporting null to make my original function false & not run the sum in the range that I am testing but if there are other values then the function should return true and the sum can be run.
Public Function NullOrErrorFalse() As Variant
Dim arrOutput() As Variant
ReDim arrOutput(n) As Variant
n = 0
For Each cell In NullOrErrorFalse()
If IsNull(cell) Then
arrOutput(n) = Null
ElseIf IsError(cell) Then
arrOutput(n) = Null
Else
arrOutput(n) = cell.Value
End If
n = n + 1
Next cell
Sub test(arrOutput())
If arrOutput() = Null Then
NullOrErrorFalse = False
Else
NullOrErrorFalse = True
End If
End Sub
End Function
At this point my function won't compile correctly and being new to VBA and programming in general, I am not sure if my issue is misuse or syntax or order of operations.
Yep, there is programming flaw due to misunderstanding VBA syntax.
Public Function NullOrErrorFalse() As Variant
Dim arrOutput() As Variant
....
For Each cell In NullOrErrorFalse()
NullOrErrorFalse is the name of the function, not the parameter you are examining. When changing it, you are actually changing the result. On the other hand, you need to provide your function with the parameter (range) that it will check.
You can do you custom sum as a User-Defined Function (UDF) in this way:
Public Function SumOrNull(r As Range) As Variant
SumOrNull = 0
For Each cel In r
If IsError(cel) Or cel = "" Or Not IsNumeric(cel.Value) Then
SumOrNull = CVErr(xlErrValue)
Exit Function
End If
SumOrNull = SumOrNull + cel.Value
Next
End Function
The idea is that as soon as there is any pattern that you dont want, your UDF raises an error, so that Excel displays #Value, and the cell using your UDF is considered as erroneous.

Modal Value, and Repetition

I want to make a modal value calculator, So it calculates the Modal value and its repetition
The idea is to make a list of data and its repetition like shown in any Graph.
This is code you I start with:
Public Class
Dim a1(100), a2(100), Rep(100), RepMer(100), AMer(100) As Single, n, count, m As Single, z, k, c, mars As Integer
n = InputBox("How many data?", "RepTest")
count = 0
For count = 0 To n - 1
a1(count) = InputBox("Add Value", "RepTest")
Next
z = n
For run = 0 To n - 1
mars = c
z = z - 1
k = 0
For moon = 0 To (n - 1)
If a1(z) = a1(moon) Then
k = k + 1
a2(run) = a1(z)
Rep(run) = Rep(run) + k
If Rep(run) > 2 Then
Rep(run) = Rep(run) - 1
End If
End If
Next
MsgBox(a2(run)), , "Modal Value")
MsgBox(Rep(run)),, "Repetition")
Next
End Class
However, If you make the massage box outside the main 2nd loop, you have to make another loop with the same value 0 To (n-1), to match data position in an Array.
what I want you to help me please is:
I have to save the modal value and the repetition of one value in same position in the array, because if you use this code the output of n = 5 and enter value 2 ,2 ,2 ,1,1. the output will be 5 massage box with 3 equal for value 2, and 2 equal for value 1.
you can test by yourself copy the code and insert it to a form in VB.net.
With pictures:
see this link: Picture that describe the result
You can see that the output is more than one for each value, so How can I store the value and its repetition in one position in an array?
Thank you for reading, please I want a code answer. :)
UPDATE
If you want count duplicates(repetitions) of values and then use it for your needs:
Create custom class where you can keep information about value and amount of repetitions
Public Class RepetitionValue
Public Property Value As Single
Public Property RepetitionAmount As Int32
Public Sub New(value As Single)
Me.Value = value
Me.RepetitionAmount = 1
End Sub
End Class
Collect values and count duplicates, save it in then Directory collection
With Directory you will easy get object by value
Use collection for manipulating data
Public Sub YourSybName()
Dim values As New List(Of Single)()
Dim numberOfData As Int32
numberOfData = Int32.Parse(InputBox("How many data?", "RepTest"))
For count As Int32 = 0 To numberOfData - 1
values.Add(InputBox("Add Value", "RepTest"))
Next
Dim modals As New Dictionary(Of Single, RepetitionValue)()
For Each value As Single In values
If modals.ContainsKey(value) = True Then
modals(value).RepetitionAmount += 1
Else
modals.Add(New RepetitionAmount(value))
End If
Next
'Use collection for your needs
'For example if I have 5 data, then I enter 1 . 5 . 1 . 2 . 1.
'then I want to divide repetition of value 1 by repetition of value 5.
Dim valueOf1 As RepetitionValue = modals(1)
Dim valueOf5 As RepetiotionValue = modals(5)
Dim divideResult As Decimal = valueOf1.RepetitionAmount/valueOf5.RepetitionAmount
End Public

changing size of 2D array with vba

I always have trouble with Arrays which is why I usually avoid them but this time I'm trying to get my head round them
I'm trying to change the size of my Global Array inside vba
I have declared it using Public UseArr() As String
Now I've written a function that searches an SQL table and returns user information as a record set.
I want to take this record set and put it into my Global Array
This is the bit of code I've written for populating it
a = 0
If Not Not UseArr Then
For i = 0 To UBound(UseArr)
If StrComp(UseArr(i, 0), rs("Alias")) = 0 Then a = 1
Next i
b = i
Else
b = 0
End If
If a = 0 Then
ReDim Preserve UseArr(0 To b, 0 To 10)
With rs
If Not .BOF And Not .EOF Then
For j = 0 To 10
If Not rs(j) = "" Then
UseArr(b, j) = rs(j)
Else
UseArr(b, j) = "Null"
End If
Next j
End If
End With
End If
The idea being if the user is already in there it doesn't populate, and if not it populates.
It works fine for initialising the Array however when I go to put in a second user it throws a resize error.
Can anyone help?
Thanks in advance
Tom
Update with Dictionary Attempt
If UseList Is Nothing Then
Set UseList = New Dictionary
MsgBox "Created New"
End If
If UseList.Exists(rs("Alias")) Then
Dim temp()
For i = 0 To 10
temp(i) = rs(i + 1)
Next i
With UseList
.Add Key:=rs("Alias"), Item:=temp
End With
End If
Debug.Print UseList
You can only Redim Preserve the last dimension of a multi-dimensional array - see here. Have you considered using a Collection or Dictionary instead?
edit: using the code you've posted above, here's how you would display element 4 from the array associated with the key "tom"
MsgBox UseList("tom")(4)
or equivalently
MsgBox UseList.Item("tom")(4)
Here you have some explanation about how a Dictionary object works and some of its attributes and functions.
I think it's the best to reach your goal because they are so easy to use, fast and efficient.
First you have to import the mscorlib.dll into the Project References.
After you can use something like this to declare the dictionary:
Dim UseDict As Dictionary
Set UseDict = New Dictionary
To know if the Key you're searching is not in the Dictionary and then add the new user:
If Not UseDict.Exists(Key) Then
UseDict.Item(Key) = 1
End If
The Value is not important here, but if you wanted to count how many times a key appears somewhere, you could increment the value when UseDict.Exists(Key) = True.
That's what the Dictionaries, Hash-maps or Maps stand for: count and search efficiently.
Hope it helps!
I attach a code with some corrections. I think the problem is that you are trying to access to an array as if it was a variable. That means you have to loop through the item of a key.
I add comments to the code below:
Update
If UseList Is Nothing Then
Set UseList = New Dictionary
MsgBox "Created New"
End If
If UseList.Exists(rs("Alias")) Then
'i think is better to specify the data type and the dimension.
Dim temp(10) as string
'if you loop from 0 to 10 the array will have 11 elements
For i = 0 To 9
temp(i) = rs(i + 1)
Next i
'This will work also and looks nicer (in my opinion) than the method
'you pasted, but if that worked anyway don't change it ;)
UseList(rs("Alias")).Item = temp
End If
Now, if you want to retrieve the result you must:
For i = 0 To UBound(UseList.Item(rs("Alias")) - 1
Debug.Print UseList.Item(rs("Alias"))(i)
Next i
Give me feedback when you test the code, please :)

Excel "Subtotal" array formula - Other form of sum.if

This is a continuation of the question excel different SUM.IF array function, But since I've marked that as solved, I created a new question.
What I wanted there was a distinct sum of some values, and I have implemented #Marc's solution. However the report requirements have changed. I now need to exclude all values that are hidden, but still keep the original calculation method. Basicly i want to add a feature in the same way a SUBTOTAL(109, ref) would work.
To this I've created a simple VBA function CellIsNotHidden(Range), which returns 0 or 1 depending on the cell.
Therefore my best guess would be a formula like: {=SUM(IF($B1:$B7<>$B2:$B8,D2:D8,0)*CellIsNotHidden(D2:D8))}
But this function doesn't work, because CellIsNotHidden is not an array function.
How can I solve this?
In advance, thanks
Gunnar
Edit:
Thought I should include the simple VBA function:
Function CellIsNotHidden(InputRange As Range)
If InputRange.Cells.Height = 0 Then
CellIsNotHidden = 0
Else
If InputRange.Cells.Width = 0 Then
CellIsNotHidden = 0
Else
CellIsNotHidden = 1
End If
End If
End Function
Try this for UDF CellIsNotHidden. This will handle 1d (vector) and 2d arrays. Tested:
Function CellIsNotHidden(MyRange As Range) As Variant
Dim RootCell As Range
Dim tmpResult() As Long
Dim i As Long
Dim j As Long
On Error GoTo Whoops
ReDim tmpResult(0 To MyRange.Rows.Count - 1, 0 To MyRange.Columns.Count - 1)
Set RootCell = MyRange.Cells(1, 1)
For j = 0 To MyRange.Columns.Count - 1
For i = 0 To MyRange.Rows.Count - 1
tmpResult(i, j) = Not (RootCell.Offset(i, j).EntireColumn.hidden Or RootCell.Offset(i, j).EntireRow.hidden)
Next i
Next j
CellIsNotHidden = tmpResult
On Error GoTo 0
Exit Function
Whoops:
Debug.Print Err & " " & Error
End Function
Instead of using the UDF CellIsNotHidden(D2:D8) you could also try either of these:
SUBTOTAL(109,OFFSET(D2,ROW(D2:D8)-ROW(D2),))
SUBTOTAL(109,OFFSET(D2:D8,ROW(D2:D8)-MIN(ROW(D2:D8)),,1))

Resources