Variables will not convert to integers - excel

Currently messing around with macros in excel. One that generates a range that fills 3 columns of cells with 1-9.
Another that plots these numbers kind of like battle ship with x,y and v for the value.
I've gotten the number generation part working and I'm stuck on the plotting bit.
Currently the issue it that I'm getting an error "13" which means that my variables don't match up.
But i'm using a CInt to convert the variant to a int.
Debugging it seems like the for loop is getting all the values properly but just not converting.
Here is the code I have so far and a screenshot of the whole thing.
Sub random()
Dim MyRange As Range
Dim c As Integer, r As Integer
Set MyRange = Workbooks("test random gen").Sheets("Sheet1").Range("G16:I30")
For c = 1 To MyRange.Columns.Count
For r = 1 To MyRange.Rows.Count
Randomize
MyRange.Cells(r, c) = Int((9 - 1 + 1) * Rnd + 1)
Next r
Next c
End Sub
Sub Button6_Click()
Dim Board As Range
Dim Table As Range
Dim c As Integer, r As Integer
Dim Xboard As Integer, Yboard As Integer, Vboard As Integer
Dim Xboardv As Variant, Yboardv As Variant, Vboardv As Variant
Set Table = Workbooks("test random gen").Sheets("Sheet1").Range("G16:G30")
Set Board = Workbooks("test random gen").Sheets("Sheet1").Range("M16:U24")
For r = 1 To Table.Rows.Count
Xboardv = Table.Cells.Value
Yboardv = Table.Cells.Offset(columnOffset:=1).Value
Vboardv = Table.Cells.Offset(columnOffset:=2).Value
Xboard = CInt(Xboardv)
Yboard = CInt(Yboardv)
Vboard = CInt(Vboardv)
Board.Cells(Xboard, Yboard).Value = (Vboard)
Next r
End Sub

Hm, oke I'll recap what I mentioned in my comments above:
"It is getting stuck on the Xboard = CInt(Xboardv) line with the error "13""
You are creating an array of values with Xboardv = Table.Cells.Value. The array is sized 1 To 15, 1 To 1 and you need to use these index numbers as row and column parameters when you refer to any element in the array. So basically: Xboard = CInt(Xboardv(<X>,<Y>)).
"That did it but now it only does it for the first value of the array due to it being called out as (1,1)"
That is because you are constantly refering to the same element. However, you have created a loop with r variable allready. So you can use that to call different elements: Xboard = CInt(Xboardv(r,1))
I've neglected the fact that your structure is somewhat strange and you are creating the same array in a loop. So move that outside your loop and possibly use a For R = Lbound(Xboard) to Ubound(Xboard) loop instead. And you can just address values without having to convert them too.
Btw, no need for Integer variables at all. Use Long instead.

What's the reason for doing a conversion to begin with?
In cell 'D4', I've put the value 325, and I've run following piece of code:
Dim b As Integer
b = Range("D4").Value
Value b is 325, no problem. No conversion needed.

Related

Table (ListObject) Assigning values using UDF

Above is a simplified example but what I want to achieve with my UDF is to accept the string in the Collated column and a delimiter which will be used to break the sting apart into substrings and be assigned sequentially to the columns Q1, Q2, Q3, Q4. It is possible for there to be less than 4 substrings generated but there will never be more than 4.
Function DECONS(Subject As String, Delim As String) As String
' takes an input string "Subject" and seperates it using "Delim" as the deliminator
' If the desired element exceeds the number of unique substrings the function returns a blank result
' Hardcoded for max 4 substrings
' initializes temporary variables
Dim i As Long
Dim r() As String
' uses built in VBA function to split the passed string using the deliminating character
r = Split(Subject, Delim)
' increases the size of r() to 4 elements and fills extra elements with blancks
For i = UBound(r) + 1 To 3
ReDim Preserve r(i)
r(i) = ""
Next i
' my sad attempt at what I want to acheive
Dim loT As ListObject
Set loT = ThisWorksheet.ListObjects("TT")
For i = 1 To 4
loT.ListColumn(i + 1).Range.Value = r(i - 1)
Next i
End Function
I've done the text manipulation using the VBA split function which yields an array of strings but I'm hitting a wall as to how to assign the substrings to the relevant columns. My attempt can be seen above. I've done a fair bit of reading but I'm still not comfortable enough in VBA/Excel to figure this out on my own yet. Is this more complex when in a ListObject than outside a table object?
Unfortunately I can't return the string array from the function and then assign it to multiple cells as the Table Object doesn't allow array operations. I had a work around where I would return a specified element, ie the 3rd, and I would call the function in each column and output the one corresponding value. However, the method is not elegant and does a lot of unnecessary repeated computation.
Try
Sub DECONS(Delim As String)
Dim objList As ListObject
Dim vDB As Variant, vSplit
Dim vR(), n As Integer, r As Long
Dim i As Long
Set objList = ActiveSheet.ListObjects("TT")
vDB = objList.DataBodyRange.Columns(1)
r = UBound(vDB, 1)
ReDim vR(1 To r, 1 To 4)
For i = 1 To r
vSplit = Split(vDB(i, 1), "\")
n = 0
For Each v In vSplit
n = n + 1
vR(i, n) = v
Next v
Next i
'Range("b2").Resize(r, 4) = vR
objList.DataBodyRange.Columns(2).Range("a1").Resize(r, 4) = vR
End Sub

How to use string names in loop like form controls?

In form controls we can use { controls("Textbox"&1) } as for loops.
My question is I have already defined the String as D1,D2,D3. I want to use like D as common and suffix as variable
sub abcd ()
dim i, k as integer
dim D1 as string
dim D2 as string
k="abcd1"
for i = 1 to 2
if k<> "" then 'like controls("textbox" & i ) for loop
"D"&i = true
Else "D" & i+1
end sub
It shows a messagebox with the error:
expected : line number or label or statement or end of statement
This code has several (actually a lot) of issues:
Dim i, k As Integer declares k As Integer but i As Variant in VBA you need to specify a type for every variable.
You declare D1 as String but True is a Boolean.
If you declare Dim D1 As String you cannot access the variabele with "D" & i = True. This is no valid syntax. Therefore you would need to use an array:
Dim MyArray(1 To 2) As Boolean
So you can access it like
MyArray(i) = True 'where i can be 1 or 2
If you declare k As Integer that means k repersents a number (without decimals). So you cannot put text in there k = "abcd1", this will produce a type missmatch. Also comparing k against a string (text) "" like If k <> "" Then will missmatch since k is a number.
Also have a look at data type summary to study which data type to use for which kind of data.
Your For i = 1 To 2 loop is missing a Next i in the end of the loop.
Your If statement is missing a End If in the end.
So in general I can only recommend you to study some tutorials or books about the basic syntax of VBA, you are lacking a lot of basic things.
So the following example might help you:
Option Explicit
Public Sub Test()
Dim i As Long
Dim MyArray(1 To 3) As Boolean
For i = 1 To 3
MyArray(i) = True
Next i
End Sub

Subtracting Variants

I am having trouble getting Variants to subtract. I am pulling data from a spreadsheet and if one cell states a phrase then I need the code to subtract one cell from another. If the cell does not state a phrase then I need it to copy one cell to another. I can get the code to run but nothing happens.
Private Sub CommandButton1_Click()
Dim x As Variant, y As Variant, z As Variant, a As Integer, B As String
'getting values for data
x = Range("D2:D48").Value
y = Range("I2:I48").Value
z = Range("E2:E48").Value
B = "Total ISU Days: "
'The the cells are empty then subtract. This is not what I wanted to do but I can't think of extracting strings from variants.
If IsEmpty(Range("D2:D48").Value) = True Then
a = y - z
End If
Range("N2:N48").Value = a
Range("M2:M48").Value = B
End Sub
x = Range("D2:D48").Value
y = Range("I2:I48").Value
z = Range("E2:E48").Value
A Variant contains metadata about its subtype. In this case, x, y, and z are all arrays of variants.
a = y - z
The right-hand side of this expression simply cannot be evaluated, because {array1} - {array2} means nothing: operators (arithmetic or logical) work off values, not array of values.
What is a supposed to be? It's declared As Integer, so its value is capped at 32,767 (should probably be a Long). If you mean to add up all the values in y and subtract that total from the sum of all values in z, then you need to be more explicit about how you do that - you could use Application[.WorksheetFunction].Sum to add things up:
sumOfY = Application.Sum(Range("I2:I48"))
sumOfZ = Application.Sum(Range("E2:E48"))
a = sumOfY - sumOfZ
And then...
Range("N2:N48").Value = a
That will put the value of a in every single cell in the N2:N48 range - is that really what you mean to do?
Or maybe you meant to do this instead?
Range("N2:N48").Formula = "=IF(D2="""",I2-E2,0)"
That would make each cell in N2:N48 calculate the difference between I and E for each row where D is empty... and there's not really any need for any VBA code to do this.
Let's simplify a bit the task and say that the idea is to substract the values in Range("C1:C6") from the corresponding values in the left - Range("B1:B6"). Then write the corresponding results in column E:
Of course, this would be done only in case that all values in column A are empty. This is one way to do it:
Sub TestMe()
Dim checkNotEmpty As Boolean: checkNotEmpty = False
Dim substractFrom As Range: Set substractFrom = Worksheets(1).Range("B1:B6")
Dim substractTo As Range: Set substractTo = Worksheets(1).Range("C1:C6")
Dim MyCell As Range
Dim result() As Variant
ReDim result(substractFrom.Cells.Count - 1)
Dim areCellsEmpty As Boolean
For Each MyCell In substractFrom
If Len(MyCell) > 0 Then checkNotEmpty = True
Next
Dim i As Long
For i = LBound(result) + 1 To UBound(result) + 1
result(i - 1) = substractFrom.Cells(i) - substractTo.Cells(i)
Next
Worksheets(1).Range("E1").Resize(UBound(result) + 1) = Application.Transpose(result)
End Sub
The code could be improved further, saving all ranges to an Array, but it works quite ok so far.
The part with the +1 and -1 in the For-loop is needed as a workaround:
For i = LBound(result) + 1 To UBound(result) + 1
result(i - 1) = substractFrom.Cells(i) - substractTo.Cells(i)
Next
because the arrays start from index 0, but the Cells in a range start with row 1.
Worksheets(1).Range("E1").Resize(UBound(result) + 1) = Application.Transpose(result) is needed, to write the values of the result array to the column E, without defining the length of the range in E.

Error with variant variable in regression output

I have a macro which is written to perform an OLS regression on data that is selected by the user. This is part of a larger add in that I am writing but I am stuck on what I think must be somewhat of a simple issue. I keep getting a subscript out of range error and I think its because I am getting a different sized matrix to what I am expecting.
The sub takes two variables as its arguments and calculated the OLS estimator given the specification. The y variable is always a n x 1 range (one column and multiple row) and the X variable is a n x m range (can be multiple columns and rows). When this function is used when X is a single column range, the For... Next block works for the following code:
For bcnt = 1 To k
Cells(bcnt, 1).Value = b(bcnt)
Next bcnt
But if the X variable is a multiple column range this won't work and it has to be the following:
For bcnt = 1 To k
Cells(bcnt, 1).Value = b(bcnt,1)
Next bcnt
I can't understand why as by my understanding b should always be a one dimensional array.
Would appreciate any help.
The actual sub:
Sub OLSregress(y As Variant, X As Variant)
Dim Xtrans, XtransX, XtransXinv, Xtransy As Variant
Dim outputsheet As Worksheet
Dim b As Variant
' The equation for this estimator is b=[X'X]^(-1)X'Y
Xtrans = Application.WorksheetFunction.Transpose(X)
XtransX = Application.WorksheetFunction.MMult(Xtrans, X)
XtransXinv = Application.WorksheetFunction.MInverse(XtransX)
Xtransy = Application.WorksheetFunction.MMult(Xtrans, y)
b = Application.WorksheetFunction.MMult(XtransXinv, Xtransy)
k = Application.WorksheetFunction.Count(b)
Set ouputsheet = Sheets.Add(, ActiveSheet)
ActiveSheet.Name = "Regression Output"
For bcnt = 1 To k
Cells(bcnt, 1).Value = b(bcnt, 1)
Next bcnt
End Sub
When you are referring to a range or are bringing in data from a sheet the array is always a 2 dimensional array. The first dimension is rows and the second is the columns.
This is a common point of confusion in VBA for excel because it's done without your intervention.
Your code is correct.
For more in-depth information check out this post

Split and sort strings components using Excel

I have a column in Excel with the format:
A01G45B45D12
I need a way to format it like this, that is divide the string into groups of three characters, sort the groups alphabetically and then join them together with a + sign between:
A01+B45+D12+G45
I wonder it this is possible using the built in formulas in Excel or if I have to do this using VBA or something else, I already have the code for this in C# if there is an easy way to use that from Excel. I have not written plugins for Excel before.
Edit to add:
The above is just an example, the string can be of "any length" but its always divisible by three and the order is random so I cannot assume anything about the order beforehand.
Sub ArraySort()
Dim strStarter As String
Dim strFinish As String
Dim intHowMany As Integer
Dim intStartSlice As Integer
strStarter = ActiveCell.Offset(0, -1).Value 'Pulls value from cell to the left
intHowMany = Int(Len(strStarter) / 3)
ReDim arrSlices(1 To intHowMany) As String
intStartSlice = 1
For x = 1 To intHowMany
arrSlices(x) = Mid(strStarter, intStartSlice, 3)
intStartSlice = intStartSlice + 3
Next x
Call BubbleSort(arrSlices)
For x = 1 To intHowMany
strFinish = strFinish + arrSlices(x) & "+"
Next x
strFinish = Left(strFinish, Len(strFinish) - 1)
ActiveCell.Value = strFinish 'Puts result into activecell
End Sub
Sub BubbleSort(list() As String)
'Taken from power programming with VBA
'It’s a sorting procedure for 1-dimensional arrays named List
'The procedure takes each array element, if it is greater than the next element, the two elements swap positions.
'The evaluation is repeated for every pair of items (that is n-1 times)
Dim First As Integer, Last As Long
Dim i As Long, j As Long
Dim temp As String
First = LBound(list)
Last = UBound(list)
For i = First To Last - 1
For j = i + 1 To Last
If list(i) > list(j) Then
temp = list(j)
list(j) = list(i)
list(i) = temp
End If
Next j
Next i
End Sub

Resources