I am trying to pull numerical grade values and convert them into a string which will represent the grade, A >90, B >80, and so on. I planned to use a for loop to gather the values from the spreadsheet and then if then statements to assign the letter designation. Here is the code I have so far.
For i = 1 To 11
' Pull Grade numerical value
Grade = Cells(2 + i, 16).Value
' Assign Numerical Value a script Grade
If Grade > 60 Then
Letter = "D"
If Grade > 70 Then
Letter = "C"
If Grade > 80 Then
Letter = "B"
If Grade > 90 Then
Letter = "A"
Else
Letter = "F"
Exit
' Print the letter grade
Cells(2 + i, 17).Text = Letter
Next i
I keep getting errors either pertaining to "Exit" or to "Next i". I have tried using "End" statements as well but that didn't fix the problems either.
Or just use a formula in column Q:
=IF(P:P>=90,"A",IF(P:P>=80,"B",IF(P:P>=70,"C",IF(P:P>=60,"D","F"))))
so it updates automatically and you don't need to use VBA.
Alternatively you can add a sheet named GradeList with the following data
and use
=INDEX(GradeList!B:B,MATCH(P:P,GradeList!A:A,1))
as formula. This way you can easily edit the numbers/grades later, and it will pick the correct grades automatically:
I would prefer Select Case in this situation. And start the Loop from 3 so you don't need to add 2:
For i = 3 To 11
' Pull Grade numerical value
Grade = Cells(i, 16).Value
' Assign Numerical Value a script Grade
Select Case Grade
Case Is >= 90: letter = "A"
Case Is >= 80: letter = "B"
Case Is >= 70: letter = "C"
Case Is >= 60: letter = "D"
Case Else: letter = "F"
End Select
' Print the letter grade
Cells(i, 17).Value = letter
Next i
Great answers from everyone, but surprised not to see a Select...Case mentioned here. This is a perfect example, perhaps even textbook.
Select...Case evaluates a variable according to a number of criteria and then does whatever is written inside the case statement.
So your code would look like this:
For i = 3 To 13
' Pull Grade numerical value
Grade = Worksheets("YourSheetNameHere").Cells(i, 16).Value 'Change that sheet name
'do our evaluation based on "grade"
Select Case Grade
Case >= 90
Letter = "A"
Case >= 80
Letter = "B"
Case >= 70
Letter = "C"
Case >= 60
Letter = "D"
Case Else
Letter = "F"
End Select
Next i
You have a bunch of problems here.
First exit needs to be end if
Second You need elseif for multiple conditions in the same if statement.
Lastly you need to reorder your ifs. If you have a grade of 90 it would return a "D" as that is the first true statement it encounters.
You might as well just loop from 3 - 13 instead of adding 2 each time aswell. And make sure you use explicit references, it will eventually bite you.
I missed one thing, Make all your comparisons >= Folks won't be happy if their 90 is a B.
For i = 3 To 13
' Pull Grade numerical value
Grade = Worksheets("YourSheetNameHere").Cells(i, 16).Value 'Change that sheet name
' Assign Numerical Value a script Grade
If Grade >= 90 Then
Letter = "A"
elseIf Grade >= 80 Then
Letter = "B"
elseIf Grade >= 70 Then
Letter = "C"
elseIf Grade >= 60 Then
Letter = "D"
Else
Letter = "F"
End if
' Print the letter grade
Worksheets("YourSheetNameHere").Cells(i, 17).value = Letter 'Change that sheet name
Next i
Alternatively, use INDEX and MATCH as Application.Function:
Sub Test()
Dim Grade As Long: Grade = 55
Dim arr1 As Variant: arr1 = Array("A", "B", "C", "D", "F")
Dim arr2 As Variant: arr2 = Array(100, 89, 79, 69, 59)
With Application
Debug.Print .Index(arr1, .Match(Grade, arr2, -1))
End With
End Sub
Or drop the INDEX function, and call the array directly:
With Application
Debug.Print arr1(.Match(Grade, arr2, -1) - 1)
End With
Obviously implement this in your loop/function and write the returned value to your cells. Change Grade variable to see the different values this would return =)
I changed the format of the code to match Jclasley and it works fine, here is my final code:
For i = 1 To 11
' Pull Grade numerical value
Grade = Cells(2 + i, 16).Value
' Assign Numerical Value a script Grade
Select Case Grade
Case Is >= 90
Letter = "A"
Case Is >= 80
Letter = "B"
Case Is >= 70
Letter = "C"
Case Is >= 60
Letter = "D"
Case Else
Letter = "F"
End Select
Cells(2 + i, 17).Value = Letter
Next i
End Sub
Related
I created this code in VBA so that every time I delete a number or the cell is empty(D7:O36), this code will run automatically(on selection change).
The code runs fine if a certain small amount of cells(~100) gets empty at once, then the cells will get filled with a "-".
The problem is that after doing it all at once more that around 100 times(each cell), Excel will stop working with error of run time error 1004. I've read about the error but it doesn't look like it applies here, at least not to the naked eye.
I don't know if the problem is how I implemented it or that i'm doing something too heavy for excel to handle.
UPDATE:
Thanks to - Tim Williams - comment bellow, the issue was not only fixed(for some reason it worked) but the code got super small and simple, AND it runs faster AND each time the "-" is added, Excel doesn't pull you to the active cell(you can activate other cell meanwhile the code is running)
Comment:
Maybe simpler: Dim c As Range: For Each c In Me.Range("D7:O36").Cells: If Len(c.Value)=0 Then c.Formula = "=""-""": Next c –
Tim Williams
Here is the updated code:
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
Dim c As Range
For Each c In Me.Range("D7:O36").Cells
If c.Value = "" Then
c.Formula = "-":
End If
Next c
End Sub
Original code:
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
Dim num As Integer
Dim letter As String
Dim count As Integer
Dim cellvalue As String
Dim cellnum As String
letter = "D"
num = 7
count = 0
For i = 0 To 432
cellnum = letter & num
cellvalue = Range(cellnum).Value
If cellvalue = "" Then
Range(cellnum).Select
ActiveCell.FormulaR1C1 = "-"
End If
If num = 36 Then
If count = 0 Then
letter = "E"
ElseIf count = 1 Then
letter = "F"
ElseIf count = 2 Then
letter = "G"
ElseIf count = 3 Then
letter = "H"
ElseIf count = 4 Then
letter = "I"
ElseIf count = 5 Then
letter = "J"
ElseIf count = 6 Then
letter = "K"
ElseIf count = 7 Then
letter = "L"
ElseIf count = 8 Then
letter = "M"
ElseIf count = 9 Then
letter = "N"
ElseIf count = 10 Then
letter = "O"
End If
num = 6
count = count + 1
End If
num = num + 1
Next i
End Sub
I have two different spreadsheets A and B where both of them have a column consisting of server names. Some of the server names are in common and for this I want to make a conditional query in Excel where I first want to check if server names are equal and if so check if a column in A consisting of integer values have a limit of 90. If the server has a corresponding integer value greater than 90 I want to give a color fill in one of the B's column. So roughly a python like pseudo-code would look like:
for i in range(Spreadsheet A: Column 1):
for j in range(Spreadsheet B: Column 1):
if i==j:
if i.column 2 > 90:
color fill j.column2 red
else:
color fill j.column2 green
I just used a couple of hours to learn the VBA syntax, so the answer would look like:
Sub Patch()
Dim s1 As Worksheet
Set s1 = Sheets("Patch")
Dim s2 As Worksheet
Set s2 = Sheets("Report")
For i = 5 To 259
For j = 2 To 227
If s1.Cells(i, 1) = s2.Cells(j, 1) Then
If s1.Cells(i, 7) > 90 Then
s2.Cells(j, 1).Interior.Color = vbRed
Else
s2.Cells(j, 1).Interior.Color = vbGreen
End If
Else
End If
Next j
Next i
End Sub
I am writing a very simple sub process to assign a letter grade to numeric grade values. I have a loop and I am trying to set the cell value to the output of my function. This seems like a very simple task but the first two iterations of my loops are not assigning any values. My Loop only goes through 4 rows.
Function get_letter(grade As Double)
Select Case grade
Case 0 To 59: letter = "F"
Case 60 To 69: letter = "D"
Case 70 To 79: letter = "C"
Case 80 To 89: letter = "B"
Case 90 To 100: letter = "A"
End Select
get_letter = letter
End Function
Sub assign_letter_grade()
Dim x As Integer
Dim grade As Range
Dim letter As Range
num_rows = Range("A2", Range("A2").End(xlDown)).Rows.Count
Set grade = Range("J2")
Set letter = Range("K2")
For x = 1 To num_rows
letter.Value = get_letter(grade.Value)
Set grade = grade.Offset(1, 0)
Set letter = letter.Offset(1, 0)
Next
End Sub
Why don't this set the values of my first two rows in the loop?
Try this:
Function get_letter(grade As Double) As String
if grade < 60 Then
get_letter = "F"
Elseif grade < 70 Then
get_letter = "D"
Elseif grade < 80 Then
get_letter = "C"
Elseif grade < 90 Then
get_letter = "B"
Else
get_letter = "A"
End If
End Function
The problem comes from the scores that is NOT included in the range. For VBA a score of 79.25 does not falls in either Case C or B. You could try to see if below would fix the problem:
Function get_letter(grade As Double)
Select Case grade
Case 0 To 59.99: letter = "F"
Case 60 To 69.99: letter = "D"
Case 70 To 79.99: letter = "C"
Case 80 To 89.99: letter = "B"
Case 90 To 100.99: letter = "A" ' assuming student can get a score over 100
End Select
get_letter = letter
End Function
Or using INDEX/MATCH
Function get_letter(grade As Double) As String
get_letter = Evaluate("INDEX({""F"",""D"",""C"",""B"",""A""},MATCH(" & grade & ",{0,60,70,80,90,100}))")
End Function
sample
Sub b()
Debug.Print get_letter(59.99)
Debug.Print get_letter(60)
End Sub
Because you start at row 2:
Set grade = Range("J2")
Set letter = Range("K2")
and then immediately offset by 1 row in your loop:
Set grade = grade.Offset(1, 0)
Set letter = letter.Offset(1, 0)
therefore missing rows 1 to 2 out and starting at Row 3. Use the x variable in your loop to correct the problem:
For x = 1 To num_rows
letter.Value = get_letter(grade.Value)
Set grade = Range("J" & x)
Set letter = Range("K" & x)
Next
I keep getting a "compile error: next without For" when I try to run this code. However, after checking everything over multiple times, I do not see how it does not recognize their presences. This is my first VBA code, so any help would be greatly appreciated.
Sub Naming()
'
' Naming Macro
' Assigns a category name in a cell based on values in a cell one column over
'
Dim number As Double
For i = 9 To 200
number = Cells(i, 3).Value
If number = 0 Then
GoTo Line1
Else
If number <= 199999 And number > 0 Then
Cells(i, 2) = "EP-GEARING"
Else
If number <= 399999 And number > 199999 Then
Cells(i, 2) = "DRIVES"
Else
If number <= 499999 And number > 399999 Then
Cells(i, 2) = "FLOW"
Else
If number <= 599999 And number > 499999 Then
Cells(i, 2) = "SPARES"
Else
If number <= 699999 And number > 599999 Then
Cells(i, 2) = "REPAIR"
Else
If number <= 799999 And number > 699999 Then
Cells(i, 2) = "FS"
Else
If number <= 899999 Then
Cells(i, 2) = "GC-GEARING"
Else
GoTo Line1
Line1:
End If
Next i
End Sub
ElseIf is one word in VB.
If number = 0 Then
'Do nothing
ElseIf number <= 199999 And number > 0 Then
Cells(i, 2) = "EP-GEARING"
ElseIf number <= 399999 And number > 199999 Then
...
Else
'Do nothing
End If
However, Select Case would fit better here:
Select Case number
Case 0
'Do nothing
Case 1 To 199999
Cells(i, 2) = "EP-GEARING"
Case 200000 To 399999
...
Case Else
'Do nothing
End Select
Your code should look like this:
Sub Naming()
'
' Naming Macro
' Assigns a category name in a cell based on values in a cell one column over
'
Dim number As Double
For i = 9 To 200
number = Cells(i, 3).Value
If number <= 199999 And number > 0 Then
Cells(i, 2) = "EP-GEARING"
ElseIf number <= 399999 And number > 199999 Then
Cells(i, 2) = "DRIVES"
ElseIf number <= 499999 And number > 399999 Then
Cells(i, 2) = "FLOW"
ElseIf number <= 599999 And number > 499999 Then
Cells(i, 2) = "SPARES"
ElseIf number <= 699999 And number > 599999 Then
Cells(i, 2) = "REPAIR"
ElseIf number <= 799999 And number > 699999 Then
Cells(i, 2) = "FS"
ElseIf number <= 899999 Then
Cells(i, 2) = "GC-GEARING"
End If
Next i
End Sub
The problem with your code as originally written is that, regardless of the Else clauses, the compiler still expects an End If for every If, and is getting confused because they are not there. The single keyword ElseIf only requires one End If statement at the end.
Goto's are seldom advisable. 99 percent of the time, there's a better and cleaner way to write it, without using a Goto.
The other answers indicate how you could fix your If statement so that VBA recognizes your For and Next pair up.
Now, personally, I would suggest using Select Case as GSerg indicated, if your loop were necessary.
But here is probably what I would do. In Cell B9 place the following formula: =IF(C9=0,"",IF(C9<=199999,"EP-GEARING",IF(C9<=399999,"DRIVES",IF(C9<=499999,"FLOW",IF(C9<=599999,"SPARES",IF(C9<=699999,"REPAIR",IF(C9<=799999,"FS",IF(C9<=899999,"GC-GEARING","")))))))) then copy it down where you need it.
Or if you want to do it with code you could replace your whole sub with no looping I could have written this as a 1 liner, but I wanted it to be legible:
Sub Naming()
'
' Naming Macro
' Assigns a category name in a cell based on values in a cell one column over
Dim theRange As Range
Set theRange = Range(Cells(9, 2), Cells(200, 2))
theRange.Value = "=IF(RC[1]=0,""""," & _
"IF(RC[1]<=199999,""EP-GEARING""," & _
"IF(RC[1]<=399999,""DRIVES""," & _
"IF(RC[1]<=499999,""FLOW""," & _
"IF(RC[1]<=599999,""SPARES""," & _
"IF(RC[1]<=699999,""REPAIR""," & _
"IF(RC[1]<=799999,""FS""," & _
"IF(RC[1]<=899999,""GC-GEARING"",""""))))))))"
'Optional if you want only the values without the formula, uncomment next line
'theRange.Value = theRange.Value
Set theRange = Nothing
End Sub
It is generally faster and cleaner to solve things like this using Excel formulas rather than writing out the logic in VBA and looping through cells.
I have a excel sheet with 2 columns
Contact Status Max Probability
80-Opp Closed Won
0-NC Closed Won
40-Pending 30-Connect
10-Working 20- Engagement
80-Opportunity 30-Connect
40-Pending 10- Engagement
I need to check if the no in contact status ie the numeric value before '-' is less than numeric value in max probabilty before '-', then i need the whole value of max probability update the contact status field
For eg.
10-Working 20- Engagement
10-Working should be replaced by 20- Engagement
OR
We can have 20- Engagement in a new column if the above mentioned conditioned is satisfied, if replacing the contact Status column is difficult
How can achieve this?
This little macro will do the trick. Just change Row to the starting row and ColA/B to the two columns you want to use.
Note that GetNum returns -1 if there is no number at the start which, in this implementation, means no copying. It was unclear what you wanted to do tith Closed Won so I chose the safest option. If you do want it copied, just return a huge number instead of -1.
Option Explicit
Function GetNum(s As String) As Integer
If Mid(s, 1, 1) < "0" Or Mid(s, 1, 1) > "9" Then
GetNum = -1
Else
GetNum = Val(s)
End If
End Function
Sub Macro1()
Dim Row As String
Dim ColA As String
Dim ColB As String
ColA = "A"
ColB = "B"
Row = "2"
While Range(ColA & Row).Value <> ""
If GetNum(Range(ColA & Row).Value) < GetNum(Range(ColB & Row).Value) Then
Range(ColA & Row).Value = Range(ColB & Row).Value
End If
Row = CStr(Val(Row) + 1)
Wend
End Sub
This was tested on:
to generate:
This macro will do:
Sub a()
ColumnContSt=1
ColumnMaxProb=2
i = 1
While (Cells(i, 2) <> "")
colB = Cells(i, ColumnMaxProb)
colA = Cells(i, ColumnContSt)
posB = InStr(1, colB, "-")
posA = InStr(1, colA, "-")
If (posB <> 0 And posA <> 0) Then
intColB = CInt(Mid(colB, 1, posB - 1))
intColA = CInt(Mid(colA, 1, posA - 1))
If (intColA < intColB) Then
Cells(i, 1) = Cells(i, 2)
End If
End If
i = i + 1
Wend
End Sub
Just change ColumnContSt = 1 and ColumnMaxProb = 2 for your actual column NUMBERS ie. A=1, B=2, etc.
HTH!
If you are flexible about the actual data, the easiest way to do this is to make two columns (can be hidden in some far away sheet):
0 NC
10 Engagement
30 Connect
...
And then use VLOOKUP(CELL_WITH_PROBABILITY, REGION_WITH_TABLE, 2, true). So if you put my small 3 element table in the upper corner of a sheet and probability in C1, it'd look like VLOOKUP(A1:B3, C1, 2, true)