435,482 Members | 3,157 Online
Need help? Post your question and get tips & solutions from a community of 435,482 IT Pros & Developers. It's quick & easy.

# Access Maximum Function: Return the second highest value across 10 fields

P: 13
The code works great except if the first field is a zero or all the fields in the row contain zeros (see pic).

Many thanks!

Expand|Select|Wrap|Line Numbers
1. Function Maximum(ParamArray FieldArray() As Variant)
2. ' Declare the two local variables.
3. Dim I As Integer
4. Dim currentVal As Variant
5. Dim secondHighest As Variant
6.
7. ' Set the variable currentVal equal to the array of values.
8.   currentVal = FieldArray(0)
9.
10. ' Cycle through each value from the row to find the largest.
11.
12.     Dim tmpArray
13.     For I = 0 To UBound(FieldArray)
14.         If FieldArray(I) > currentVal Then
15.         currentVal = FieldArray(I)
16.         End If
17.     Next
18.
19. tmpArray = Filter(FieldArray, currentVal, False, vbTextCompare)
20. 'This will fill the tmpArray with all your array values EXCEPT the highest one.
21.
22.     secondHighest = tmpArray(0)
23.         For I = 0 To UBound(tmpArray)
24.         If tmpArray(I) > secondHighest Then
25.         secondHighest = tmpArray(I)
26.     End If
27.     Next
28.
29. ' Return the maximum value found.
30. Maximum = secondHighest
31.
32. ' Expr1: Maximum ([nPP1CSF],[nPP2CSF],[nPP3CSF],[nPP4CSF],[nPP5CSF],[nPP6CSF],[nPP7CSF],[nPP8CSF],[nPP9CSF],[nPP0CSF])
33.
34. End Function

Attached Images
 zMAX-SECOND.jpg (48.9 KB, 433 views)
Mar 3 '18 #1
30 Replies

 100+ P: 214 Liam, Welcome to Bytes! There are a couple issues keeping you from the answer you want. First, anytime all the numbers are the same (not just zeroes), the code won't be able to handle it because you're filtering out all the variables. You can solve this by counting how many times the numbers are the same--if they are always the same, just force insert what you want into the field (since there is no second-highest number, I'm not sure what this would be). Also, you'll run into some interesting problems if you leave the values as variables. I changed them to integers in the code below. Expand|Select|Wrap|Line Numbers Function Maximum(ParamArray FieldArray() As Variant) ' Declare the two local variables. Dim I As Integer Dim currentVal As Integer Dim secondHighest As Integer Dim intCount As Integer   ' Set the variable currentVal equal to the array of values. currentVal = FieldArray(0)   ' Cycle through each value from the row to find the largest. For I = 0 To UBound(FieldArray)     If FieldArray(I) > currentVal Then         currentVal = FieldArray(I)       ElseIf FieldArray(I) = currentVal Then         intCount = intCount + 1     End If Next   If intCount - 1 = UBound(FieldArray) Then     ' Return the starting value since they were all the same     Maximum = FieldArray(0) 'MODIFY THIS TO WHATEVER YOU WANT WHEN THEY ARE ALL THE SAME   Else     Dim tmpArray As Variant     tmpArray = Filter(FieldArray, currentVal, False, vbTextCompare)     ' This will fill the tmpArray with all your array values EXCEPT the highest one.       secondHighest = tmpArray(0)       For I = 0 To UBound(tmpArray)         If tmpArray(I) > secondHighest Then             secondHighest = tmpArray(I)         End If     Next       ' Return the maximum value found.     Maximum = secondHighest End If   ' Expr1: Maximum ([nPP1CSF],[nPP2CSF],[nPP3CSF],[nPP4CSF],[nPP5CSF],[nPP6CSF],[nPP7CSF],[nPP8CSF],[nPP9CSF],[nPP0CSF])   End Function   Mar 5 '18 #2

 Expert Mod 2.5K+ P: 3,284 liamthequietman, If you will indulge me the opportunity to modify gnawoncents's excellent suggestion.... Expand|Select|Wrap|Line Numbers Function Maximum(ParamArray FieldArray() As Variant) On Error GoTo EH     ' Declare the two local variables.     Dim I As Integer     Dim currentVal As Integer     Dim secondHighest As Integer     Dim intCount As Integer       ' Set the variable currentVal equal to the array of values.     currentVal = FieldArray(0)       ' Cycle through each value from the row to find the largest.     For I = 0 To UBound(FieldArray)         If FieldArray(I) = currentVal Then intCount = intCount + 1         If FieldArray(I) > currentVal Then             secondHighest = currentVal             currentVal = FieldArray(I)         Else             If FieldArray(I) > secondHighest _                 And FieldArray(I) < currentVal Then                 secondHighest = FieldArray(I)             End If         End If     Next       If intCount - 1 = UBound(FieldArray) Then         ' Return the starting value since they were all the same         Maximum = FieldArray(0) 'MODIFY THIS TO WHATEVER YOU WANT WHEN THEY ARE ALL THE SAME     Else         ' Return the maximum value found.         Maximum = secondHighest     End If       Exit Function EH:     MsgBox "There was an error finding the second highest value!  " & _         "Please contact your Database Administrator.", vbCritical, "WARNING!"     Exit Function End Function If you look closely above, I've eliminated the need to go through your array twice. Assuming your beginning highest value is in `currentVal` (which will begin at 0), then if there is any value greater than that, the higher value becomes `currentVal`. But, before we do that, we asign the value of `currentVal` to `secondHighest` (which initially will also be 0). Then if any future values are greater than `currentVal`, then `secondHighest` takes on the value of the previous highest value. There is an `Else` added to the code to handle cases in which the new value is less than `currentVal` but greater than `secondHighest`. This should give you a more streamlined code (and faster, at that). Hope this hepps! Mar 5 '18 #3

 100+ P: 214 twinnyfo, Very nice--much more elegant. *initiates slow clap* Mar 5 '18 #4

 Expert Mod 2.5K+ P: 3,284 gnawoncents, Aww shucks.... I just stole what you posted! :-P Mar 5 '18 #5

 P: 13 BRILLIANT! What an excellent collaboration between the two of you! I hope others can benefit from this excellent code. I'm very grateful! Mar 5 '18 #6

 Expert Mod 2.5K+ P: 3,284 Glad the two of us could hepp! Mar 5 '18 #7

P: 13
I've made changes to the code for "Minimum" values "Second Lowest" The values are Single and contain -1

It works perfect except if a -1 appears in any of the columns.

Greatly appreciated!

(see pic)

Expand|Select|Wrap|Line Numbers
1. Function Minimum(ParamArray FieldArray() As Variant)
2.     On Error GoTo EH
3.         ' Declare the two local variables.
4.         Dim I As Integer
5.         Dim currentVal As Single
6.         Dim secondLowest As Single
7.         Dim intCount As Integer
8.
9.         ' Set the variable currentVal equal to the array of values.
10.         currentVal = FieldArray(0)
11.
12.         ' Cycle through each value from the row to find the lowest.
13.         For I = 0 To UBound(FieldArray)
14.             If FieldArray(I) = currentVal Then intCount = intCount + 1
15.             If FieldArray(I) < currentVal Then
16.                 secondLowest = currentVal
17.                 currentVal = FieldArray(I)
18.             Else
19.                 If FieldArray(I) < secondLowest _
20.                     And FieldArray(I) > currentVal Then
21.                     secondLowest = FieldArray(I)
22.                 End If
23.             End If
24.         Next
25.
26.         If intCount - 1 = UBound(FieldArray) Then
27.             ' Return the starting value since they were all the same
28.             Minimum = FieldArray(0) 'Modify this to whatever you want when they are all the same
29.         Else
30.             ' Return the Minimum value found.
31.             Minimum = secondLowest
32.         End If
33.
34.         Exit Function
35. EH:
36.         MsgBox "There was an error finding the second Lowest value!  " & _
38.         Exit Function
39.
40. ' Expr1: Minimum([nPP1SHF],[nPP2SHF],[nPP3SHF],[nPP4SHF],[nPP5SHF],[nPP6SHF],[nPP7SHF],[nPP8SHF],[nPP9SHF],[nPP0SHF])
41.
42. End Function
Attached Images
 zMIN-SECOND.jpg (58.3 KB, 30 views)
Mar 5 '18 #8

 Expert Mod 2.5K+ P: 3,284 All you need to do is reverse you ">" and "<" operators. Mar 5 '18 #9

 P: 13 I edited my post. Please read again I almost have the solution! Thanks! Mar 5 '18 #10

 Expert Mod 2.5K+ P: 3,284 liam - for future posts, please use Code Tags when posting your code. The moderators are able to edit your content, but we shouldn't have to do that every time. Mar 5 '18 #11

 Expert Mod 2.5K+ P: 3,284 What happens when there is a -1 in any of the values. It should work with positive or negative numbers. Mar 5 '18 #12

 P: 13 I'm a novice and will now Google "Code Tags" to understand what they are. My apologies! Mar 5 '18 #13

 P: 13 My changes to the code work perfect except if a -1 appears in any column. see pic in post I edited Mar 5 '18 #14

 Expert Mod 2.5K+ P: 3,284 Code tags are what you place your code in so that they are properly formatted on this forum. There is a button in the text entry display "[CODE/] which will insert code tags around any of your selected text. Your pic does not tell me what the code is doing wrong. It looks to me to be doing exactly what the code is telling it to do. What do you want the result to be when it is all -1? Are these by chance text values? Mar 5 '18 #15

P: 13
If there is a column(s) with a -1 it is returning the lowest number, not the second lowest.

Columns without -1 are returning the correct second lowest number

Columns with all -1 returns -1 which is perfect
Attached Images
 Capture.jpg (59.8 KB, 30 views)
Mar 5 '18 #16

 Expert Mod 2.5K+ P: 3,284 Have you stepped through the code while it is running to see how the evaluations of the values are working? Particularly, when there is a -1 value and the code gets here: Expand|Select|Wrap|Line Numbers If FieldArray(I) < currentVal Then     secondLowest = currentVal     currentVal = FieldArray(I) Else     If FieldArray(I) < secondLowest _         And FieldArray(I) > currentVal Then         secondLowest = FieldArray(I)     End If End If How are the evaluations responding? Not sure if it has anything to do with it, but you may need to declare your array as a Double? Expand|Select|Wrap|Line Numbers Function Minimum(ParamArray FieldArray() As Double) Mar 5 '18 #17

 P: 13 Being a complete VBA novice, I'm not familiar on how to step through the code. I placed my cursor in the code and pressed F8 and received a ping which I associate with an error. Declaring the array as Double resulted in a error Mar 5 '18 #18

 Expert Mod 2.5K+ P: 3,284 No worries, we've all been novices at one point. Before you run your code, click your mouse to the left of the first line of your function in the VBA editor. It should leave a dark brown circle in the left margin. Run your code and the code should stop at that point (called a break point). Then, hit F8 repeatedly to watch the code work through itself. Any time the code is paused, you can hover your mouse over any of the variables and you will be able to see their current values. Hope that hepps! Mar 5 '18 #19

P: 13
Thanks for not giving up on this project!

My step through attempt did not work, a Macro box appears (see pic).

I found another issue with the results. In review:

CORRECT

Columns without -1 are returning the correct second lowest number

Columns with all -1 returns -1 which is perfect

INCORRECT

If there is a column(s) with a -1 it is returning the lowest number, not the second lowest.

If there are columns with identical numbers it's returning zero
Attached Images
 code.jpg (41.5 KB, 29 views)
Mar 5 '18 #20

 Expert Mod 2.5K+ P: 3,284 By "running your code", I mean you have to execute it the way you would normally execute it. Then, when the code hits your break point, then you can step through it using F8. Hope that hepps. Mar 5 '18 #21

 Expert Mod 2.5K+ P: 3,284 Or, maybe try using the values -2 and -1.2 to see if that will have any effect on the logic the code returns. At this point, I am grasping at straws, because based upon what I am seeing int he code, it "should" work, but it just doesn't. I must be missing something obvious, which would not be the first time something like that happened. Mar 5 '18 #22

 Expert Mod 2.5K+ P: 3,284 Was able to do some testing and proofing. Note lines 23-31. We forgot about the situation when the first number is the lowest. We never checked to see if the second was still the lowest value. This should solve it. Expand|Select|Wrap|Line Numbers Public Function Minimum(FieldArray() As Variant) As Single On Error GoTo EH     ' Declare the two local variables.     Dim I               As Integer     Dim currentVal      As Single     Dim secondLowest    As Single     Dim intCount        As Integer       ' Set the variable currentVal equal to the array of values.     currentVal = FieldArray(0)     secondLowest = FieldArray(0)       ' Cycle through each value from the row to find the lowest.     For I = 0 To UBound(FieldArray)         If FieldArray(I) = currentVal Then intCount = intCount + 1         If FieldArray(I) < currentVal Then             secondLowest = currentVal             currentVal = FieldArray(I)         ElseIf secondLowest = currentVal _             And FieldArray(I) > secondLowest Then                 secondLowest = FieldArray(I)         Else             If secondLowest = currentVal _                 And FieldArray(I) > secondLowest Then                 secondLowest = FieldArray(I)             Else                 If FieldArray(I) < secondLowest _                     And FieldArray(I) > currentVal Then                     secondLowest = FieldArray(I)                 End If             End If         End If     Next       If intCount - 1 = UBound(FieldArray) Then         ' Return the starting value since they were all the same         Minimum = FieldArray(0) 'Modify this to whatever you want when they are all the same     Else         ' Return the Minimum value found.         Minimum = secondLowest     End If       Exit Function EH:     MsgBox "There was an error finding the second Lowest value!  " & _         "Please contact your Database Administrator.", vbCritical, "WARNING!"     Exit Function End Function Mar 5 '18 #23

 P: 13 Can I attach a test version of my database? Mar 5 '18 #24

 Expert Mod 2.5K+ P: 3,284 Check my last post and see if that code works. It ran fine for me. I knew I was overlooking something obvious. It wasn't until I was able to step through the code that I found it. Mar 5 '18 #25

 P: 13 I applaud you for your tenacity! The expression I enter into the query has worked in all your codes except now. Error wrong number of arguments: Expr1: Minimum([nPP1SHF],[nPP2SHF],[nPP3SHF],[nPP4SHF],[nPP5SHF],[nPP6SHF],[nPP7SHF],[nPP8SHF],[nPP9SHF],[nPP0SHF]) Mar 5 '18 #26

 Expert Mod 2.5K+ P: 3,284 I changed Line 1 from: Expand|Select|Wrap|Line Numbers Public Function Minimum(ParamArray FieldArray() As Variant) As Single to: Expand|Select|Wrap|Line Numbers Public Function Minimum(FieldArray() As Variant) As Single Not sure if that makes a difference. I couldn't get it to work any other way. I was using my code from within VBA, you are calling it as an expression in a Query, which is causing the disconnect. Mar 5 '18 #27

 P: 13 I found a code which works perfect EXCEPT ... If there is a column(s) with a -1 it is returning the lowest number, not the second lowest. Otherwise all the result are correct. Expand|Select|Wrap|Line Numbers Function SecondMinimum(ParamArray FieldArray() As Variant) As Variant     Dim I As Integer     Dim LowestVal As Variant     Dim secondVal As Variant   '   Default values     LowestVal = Null     secondVal = Null   '   Populate the LowestVal and secondVal temporary variables     For I = 0 To UBound(FieldArray)         If IsNull(FieldArray(I)) = False Then             If IsNull(LowestVal) Then                 LowestVal = FieldArray(I)             ElseIf IsNull(secondVal) Then '               Before populating the secondVal, make sure LowestVal will be '               less than or equal to secondVal.                 If FieldArray(I) > LowestVal Then                     secondVal = FieldArray(I)                 Else                     secondVal = LowestVal                     LowestVal = FieldArray(I)                 End If                 Exit For             End If         End If     Next I   '   Make sure there are at least two non-Null values in the temporary variables     If IsNull(LowestVal) = False And IsNull(secondVal) = False Then   '       Loop through the array values, comparing them to the lowest and second-lowest         For I = 0 To UBound(FieldArray) '           Skip duplicate values.             If FieldArray(I) <> LowestVal Then '               New lowest value?                 If FieldArray(I) < LowestVal Then '                   New lowest value.  Push the Lowest up to second, then make this '                   array value the new lowest value.                     secondVal = LowestVal                     LowestVal = FieldArray(I) '               Skip duplicate values.                 ElseIf FieldArray(I) <> secondVal Then '                   New second-lowest value?                     If FieldArray(I) < secondVal Then '                       Replace the second-lowest value with the current array value.                         secondVal = FieldArray(I)                     End If                 End If             End If         Next I     End If   '   This function will return Null if less than 2 non-Null values were passed.     SecondMinimum = secondVal       ' Expr1: SecondMinimum([nPP1SHF],[nPP2SHF],[nPP3SHF],[nPP4SHF],[nPP5SHF],[nPP6SHF],[nPP7SHF],[nPP8SHF],[nPP9SHF],[nPP0SHF])   End Function   Mar 5 '18 #28

 Expert Mod 2.5K+ P: 3,284 Did you adjust your code to match what I provided? It worked perfectly for me with all values. Mar 5 '18 #29

 P: 13 I just ran the code in post #23. When I place the expression into the query I receive, error wrong number of arguments: Expr1: Minimum([nPP1SHF],[nPP2SHF],[nPP3SHF],[nPP4SHF],[nPP5SHF],[nPP6SHF],[nPP7SHF],[nPP8SHF],[nPP9SHF],[nPP0SHF]) Mar 5 '18 #30

 Expert Mod 2.5K+ P: 3,284 Did you change line 1 as discussed in post #27? Mar 6 '18 #31