VB Console Variable Name Loop Concatenation - arrays

I am new to visual basic at college and I am using the purely console-based version of the software. I have been set a task to, without using an array, allow the user to input 5 student names and their respective test scores and store them in 10 separate variables, using a loop. The program must then output the highest test score and the student it is assigned to.
I know that I have to use a for loop for this and have been trying to concatenate the i (assuming for i = 1 to 5) with variable names and then using console.readline to assign the variables to a value. This has been to no avail so far and I don't know what to do. Any help is appreciated.
Thanks,
George

without using an array
That's quite odd. I'll assume that also means any collection.
have been trying to concatenate the i (assuming for i = 1 to 5) with variable names.
There really isn't a good way to do that, not without some kind of code generation. You could use fields and reflection, but thats way, way out of scope most likely.
The easiest thing to do then, is just use If on the loop variable and assign the correct variable based on what i is.
Dim name1 As String, name2 As String, name3 As String, name4 As String, name5 As String
Dim score1 As Integer, score2 As Integer, score3 As Integer, score4 As Integer, score5 As Integer
For i As Integer = 1 To 5
Console.Write("Name: ")
Dim name = Console.ReadLine()
Console.Write("Score: ")
'TODO: Error checking
Dim score = Int32.Parse(Console.ReadLine())
If i = 1 Then
name1 = name
score1 = score
ElseIf i = 2 Then
name2 = name
score2 = score
End If
'Etc
Next
'Calculate check the variables for the highest score
It's crude, but it meets the criteria. If a Dictionary or List were allowed, that makes things much simpler:
Dim people As New Dictionary(Of String, Integer)
For i As Integer = 1 To 5
Console.Write("Name: ")
Dim name = Console.ReadLine()
Console.Write("Score: ")
'TODO: Error checking
Dim score = Int32.Parse(Console.ReadLine())
people.Add(name, score)
Next
'Calculate the highest score on the dictionary

If the requirement is that you must use five separate variables because you are not allowed to use an enumerable list of any kind, then I would recommend creating two methods that allow you to get and set the values by index:
Private _name0 As String
Private _name1 As String
Private _name2 As String
Private _name3 As String
Private _name4 As String
Private Function GetName(index As Integer) As String
Select Case index
Case 0
Return _name0
Case 1
Return _name1
Case 2
Return _name2
Case 3
Return _name3
Case 4
Return _name4
End Select
Throw New ArgumentOutOfRangeException()
End Function
Private Sub SetName(index As Integer, name As String)
Select Case index
Case 0
_name0 = name
Case 1
_name1 = name
Case 2
_name2 = name
Case 3
_name3 = name
Case 4
_name4 = name
End Select
Throw New ArgumentOutOfRangeException()
End Sub
Then you could do the same for the scores. Since .NET is not a dynamically typed language, it doesn't easily support accessing variables by a dynamically built string name. While it's technically feasible to accomplish something like that via Reflection, I doubt that's the intended purpose of the exercise.

Related

VBA Calculate values to Range within Function

I am trying to generate a simplistic Excel VBA function to calculate different types of interpolation. One difficulty I have is that, I don't seem to be able to re-assign the Y-range of values within the function.
When my VBA function reaches the following line, it doesn't seem to be able to change/re-calculate the YRange value according to the formula:
YRange(rowCount) = -Log(YRange(rowCount)) / XRange(rowCount)
Can anyone advise/help?
Public Function Kian_CurveInterp(XRange As Range, YRange As Range, XFrac As Double, InterpMode As Integer)
Select Case InterpMode
Case 1 'step-straight
Kian_CurveInterp = Linterp2(XRange, YRange, XFrac)
Case 2 'zero-rate interp
Dim rowCount As Integer
For rowCount = 1 To XRange.Count
YRange(rowCount) = -Log(YRange(rowCount)) / XRange(rowCount) 'convert discountFactors to zero-rates
Next rowCount
Kian_CurveInterp = Linterp2(XRange, YRange, XFrac)
Kian_CurveInterp = Exp(-Kian_CurveInterp * XFrac)
End Select
End Function

Extracting unique values from row in Excel

I need to summarize unique values from a row into a column that's in the same row. My goal is in the second row of the attached image where T:Z contains the data and AA:AC contains the summary (I typed the values in for the demo). The first row is what is currently occurring where I tried using a nested if function for values greater than zero, but I also tried using an index match function to no avail. The issue is I either receive duplicates in AA:AC or not all values are included.
Currently using Excel 2016
So if I understand you correctly, you are going to have a sheet of rows of data. You want to look in the columns T:Z and then generate a list of unique values (non-zero) in the columns AA:AC. I assume that you know you will never have more than 3 unique values, but I can't be sure that this wasn't just an omission.
Either way, the below code should work:
Sub Find_Uniques()
Dim X As Integer, Y As Integer, Z As Integer
Dim Temp_Strings() As String
For X = 1 to 10000 'This assumes you don't have more than 10,000 rows of data
ReDim Temp_Strings(1 to 5) As String
For Y = 20 to 26
If Range(Cells(X,Y).Address).Value <> "" And Range(Cells(X,Y).Address).Value <> 0 Then
For Z = 1 to 5
If Temp_Strings(Z) = "" Then
Temp_Strings(Z) = Range(Cells(X,Y).Address).Value
Exit For
End If
If Temp_Strings(Z) = Range(Cells(X,Y).Address).Value Then Exit For
Next Z
End If
Next Y
For Z = 1 to 5
If Temp_Strings(Z) <> "" Then Range(Cells(X,Z+26).Address)).Value = Temp_String(Z)
Next Z
Next X
End Sub
Thank you all for your help. Instead of extracting the data from the row, I wrote a macro that changed the zeros to blanks, deleted the blank cells, and shifted them to the left. After that it was easy to cut the range and paste it into the old data set to be analyzed.
Sub clean_data()
Sheets("Reason data").Range("H:Z").Replace 0, ""
Call delete_blanks
End Sub
Sub delete_blanks()
Sheets("Reason data").Range("H:Z").SpecialCells(xlCellTypeBlanks).Delete (xlToLeft)
Call move_data
End Sub
Sub move_data()
'Copies reason data and pastes it into data worksheet
Sheets("Reason data").Range("A3:K3", Sheets("Reason data").Range("A3:F3").End(xlDown)).Cut _
Sheets("Data").Range("A1").End(xlDown).Offset(1)
End Sub

Parsing currency value in Excel VBA

Ok... I guess my brain has finally gone on vacation without me today. I have extracted 2 fields from a website and I get this string
vData = "Amount Owed [EXTRACT]$125.00[EXTRACT]
vData was declared as an array (string).
I split vData on [EXTRACT] and I end up with 2 string variables like this:
varA = "Amount Owed"
varB = "$125.00"
To parse varB, I use
Dim varC as Currency
varC = val(varB)
I want to use varC in an If statement:
If Val(VarC) <> 0 Then
I was expecting varC to be 125 but when I look at varC is 0. I can't figure out why varC = 0 and not 125.
Use CCur(varB) instead of Val(varB). CCur converts to a Currency type, so knows about $ and other things.
Explanation from the MS docs:
... when you use CCur, different decimal separators, different thousand separators, and various currency options are properly recognized depending on the locale setting of your computer.
Val is only looking for straight numbers:
The Val function stops reading the string at the first character it can't recognize as part of a number. Symbols and characters that are often considered parts of numeric values, such as dollar signs and commas, are not recognized.

Add value to the last empty cell in a defined dynamic column

My model takes two numbers from one sheet, adds the average to another sheet in the last cell of a defined column. The problem that I have is that when I insert a new column, the references get missed up and I'm trying to have a macro that would 1. take the average 2. look for a specific column on the second sheet 3. paste the averaged value to the last cell.
Please help me with this I have been trying to get my head around it for a long time.
my problem is that I have to insert new columns and I need to keep the references dynamic when adding a value to the last empty cell in a column. For example: if i have salary as col A, and expenses as Col B - in this model that I have now I put in .Cells(emptyRow, 1) and .Cells(emptyRow, 2) now if I insert a column between A and B the references 1 and 2 will not work. Is there anyway that I can work around this where if i add a new column it wont mess up the references in the macro?
Thank you.
This is the code that I have right now but it does not really work - when I insert a new column the column defined name does not shift right.
Sub demo()
Dim expCol As Long, FirstEmptyRow As Long
Range("B:B").Cells.Name = "expenses"
expCol = Range("expenses").Column
FirstEmptyRow = Cells(Rows.Count, expCol).End(xlUp).Row + 1
Cells(FirstEmptyRow, expCol).Value = 123
End Sub
P.S. 123 here is just an example for testing purposes. The value that would replace it in my model is the average I talk about in the question.
If your columns have headers (I guess they do), and your data has no gaps just use
Range("1:1").Find(columnName).End(xlDown).Offset(1,0) = 123
If a column can have just a header but no values, you need to add additional check if second row isn't empty.
If you create a named range this way (rather than the Range.Cells.Name way you were using), then when inserting columns the reference will be dynamic. Now if you insert columns between A and B later in the code, you can still use expCol and FirstEmptyRow to reference the first empty cell in the expenses column, where ever it may have moved to on the sheet, as long as you update them after each column insertion.
Sub Demo()
Dim expensesrng As Range
Dim Expenses As Range
Dim expCol As Long
Dim Exprng As Range
Dim FirstEmptyRow As Long
'set the original range to use for the expense column
Set expensesrng = Range(Range("B1"), Range("B1").End(xlDown))
'add the named range
ActiveWorkbook.Names.Add Name:="Expenses", RefersTo:=expensesrng
' create a variable to refer to the Expenses Range
Set Exprng = ActiveWorkbook.Names("Expenses").RefersToRange
expCol = ActiveWorkbook.Names("Expenses").RefersToRange.Column
FirstEmptyRow = Exprng.End(xlDown).Offset(1, 0).Row
Cells(FirstEmptyRow, expCol).Value = 123
'after inserting columns then you will have to get/update the column number
'of the expense named range and the first empty row before adding your new expense
'data to it
Range("B:B").Insert Shift:=xlShiftToRight
expCol = ActiveWorkbook.Names("Expenses").RefersToRange.Column
FirstEmptyRow = expensesrng.End(xlDown).Offset(1, 0).Row
Cells(FirstEmptyRow, expCol).Value = 123
End Sub

Lcase operation on array taking too long

I'm working with a script designed to compare values returned from a form against values from a database dumped to an array, via GetRows. The purpose of the check is to compare form values against database values and only update the matching ids' rows in the database.
I've seen this done with hidden variables in forms, but as we have quite a few users online at any given time, the values on the db end could change while a user was completing the form.
Currently, the code uses an inner and outer loop to run this comparison, with a temporary variable being assigned the current col/row from the aforementioned array. An lcase and trim operation are performed on the value to obtain the temporary variable.
This is causing a considerable performance drain, and I was wondering if the lcase/trim functionality could perhaps be performed during the creation of that array, rather than in a looping situation?
Here's my code:
**note: this utilizes the FastString Class for concatenation, thus the "FastString" and ".Append"
dim iRowLoop, iColLoop, zRowLoop, strChange, tempDbValsCase
Set strChange = New FastString
for iRowLoop = 0 to ubound(arrDbVals, 2)
for zRowLoop = 0 to ubound(arrFormComplete)
''#****below line is what is causing the bottleneck, according
''#****to a timer test
tempDbValsCase = lcase(trim(arrDbVals(1, iRowLoop)))
''#****
if (mid(trim(arrFormComplete(zRowLoop)),1,8) = trim(arrDbVals(0, iRowLoop))) AND (mid(trim(arrFormComplete(zRowLoop)),9) <> tempDbValsCase) then
dim strFormAllVals
strFormAllVals = arrFormComplete(zRowLoop)
strChange.Append strFormAllVals & ","
end if
next
next
On the database side (MS SQL Server 2008), the table from which the array is derived through GetRows contains the bit datatype column "Complete". The lcase and trim operations are performed upon this column of the array. Does the bit datatype add any hidden characters in the output? Visually, I don't detect any, but when I compare a value of "True" from the form input against a value from the array that looks like "True," it doesn't match, until I run the lcase and trim on the "Complete" column.
Try
dim iRowLoop, iColLoop, zRowLoop, strChange, tempDbValsCase
dim iCount1, iCount2, match
Set strChange = New FastString
iCount1 = ubound(arrDbVals, 2)
iCount2 = ubound(arrFormComplete)
for iRowLoop = 0 to iCount1
for zRowLoop = 0 to iCount2
' Assign array lookup to a variable '
tempDbValsCase = arrDbVals(1, iRowLoop)
' ...and then perform operations on it one at a time '
tempDbValsCase = trim(tempDbValsCase)
tempDbValsCase = lcase(tempDbValsCase)
' Assign this array lookup to a variable and perform trim on it '
match = trim(arrFormComplete(zRowLoop))
if (mid(match,1,8) = trim(arrDbVals(0, iRowLoop))) AND (mid(match,9) <> tempDbValsCase) then
strChange.Append match & ","
end if
next
next

Resources