Getting Max number from multiple tables? - vba

I have multiple tables in an access project named PrivateCustomerInfo, BusinessCustomerInfo and NHCustomerInfo, each table has a column named "customerid" and I have a separate form that is used to add new customers into the designated table with a incremental customer ID. To do this I used Nz(DMax("[customerid]","PrivateCustomerInfo and so on for each table to basically get the largest customer ID from all the 3 tables. However, I am unsure how to compare these 3 numbers together to find the largest one and then increment by one for the new addition. I read online that you could do Max(value1, value2, value3...) but this doesnt work on Access VBA, any suggestions?

I have have written my own function that can be called with a varying number of arguments and which returns the greatest of them
Public Function MaxVal(x As Variant, ParamArray y() As Variant) As Variant
Dim max As Variant, i As Long
max = x
For i = LBound(y) To UBound(y)
If IsNull(max) Then
max = y(i)
ElseIf y(i) > max Then
max = y(i)
End If
Next i
MaxVal = max
End Function

Related

Ranking a dynamic table range by size

I have a dynamic table range of certain values (amounts). These amounts are generated into the table through a macro I've created.
What I want to do: Rank these amounts into the empty column by number.
eg. the cell in Column G next to 89k would be ranked as 1, one next to 77k would be 2 etc.
I also already have other functions defined, which I'm not going to explain here for readability reasons, but all you need to know: there are two variables obtained through functions
tbl_first = (int) Index of the ListRow of the first table item (so in this case it would be the row with 89k = 1st row so in this example 1)
tbl_last = (int) same as above, but indexes the last row (77k) in this example as 7
so my code is the following
' sets the tbl variable to the red table in the picture
Dim tbl As ListObject: Set tbl = Sheets("Summary").ListObjects("time_top")
Dim pos As Integer, diff as integer
diff = tbl_last - tbl_first
For j = tbl_first To tbl_last ' loops through all the added rows
For n = 1 to diff' indexing for the large function
' index the pos through the excel large function for our values (should return the k-th position from the largest value)
pos = Application.WorksheetFunction.Large(Range(Cells(tbl_first, 6), Cells(tbl_last, 6)), n)
With tbl.ListRows(1)
.Range(j, 6) = pos ' add the value to the column G to the right
End With
Next n
Next j
So the expected result would look like this:
I also keep getting the following error, which is caused by me incorrectly assigning the pos value.
Either way, probably multiple of things wrong here and much more elegant solution is out there, that just didn't hit me yet.
Think you need Rank (watch out for equal ranks). Large returns the nth largest value of a set.
Here is a simple example on a two column table which perhaps you can adapt. The rank is added in the second column.
Sub xx()
Dim tbl As ListObject: Set tbl = Sheets("Summary").ListObjects("time_top")
Dim r As Range
For Each r In tbl.ListColumns(1).DataBodyRange
r.Offset(, 1) = WorksheetFunction.Rank(r, tbl.ListColumns(1).DataBodyRange)
Next r
End Sub

return number of unique dates on another sheet in the same workbook

I have a project for work where I am trying to calculate the total number of days an employee worked, with data from one sheet feeding the number calculated on the same sheet, or another sheet in the same workbook.
There are several rows of data for each work day, and so I am looking to calculate the total number of unique dates for each employee separately. If the data is isolated to one employee then I can use:
=SUM(IF(FREQUENCY(B:B,B:B)>0,1))
to calculate the number of work days. I have tried using several additional formulas to get the value I'm looking for, including:
=SUMPRODUCT((TEXT('Data'!$A$1:$A$100, "yyyymm")="201804")*('Data'!B$1:$B$100="John Doe"))
AND
=SUMIFS(IF(FREQUENCY('Sono Detail'!$B:$B,'Sono Detail'!$B:$B)>0,1),'Sono Detail'!$E:$E,'(Test) Sono Report Card 1.0'!$B$3)
Neither of these worked.
This isn't a code solution (yet), but I'm posting this answer to illustrate the data you can derive from a pivot table based on the sample you provided.
In the first arrangement of fields, you can see that the pivot shows how many procedures were performed on each day for each employee. This includes an overall total number of procedures for each employee for the time period. So "Employee 1" performed 14 procedures on 1-March, and so on.
If I change the Values field of the pivot, I can show the total time (in DAYS) that each employee spent performing procedures. So in the table below, Employee 1 worked 0.15833 Days on 1-March. This translates to 288 minutes (or 3.8 hours).
VBA code to traverse the pivot table would then identify each employee and how many days they worked and can access other summative data as well.
If you can use VBA macros, then try this:
in cell L3 type:
=WorkingDaysCount(B:B,C:C,K3)
Do similarly for the other employees in L4, L5, and L6.
Then use this code below in a new code module
Option Explicit
Public Function WorkingDaysCount(ByRef Dates As Range, ByRef EmployeeRange As Range, ByVal Employee As String) As Integer
Dim ct As Integer 'counter
Dim i As Long
'for looping
Dim EmployeeDates() As Date 'array to track which dates are already counted
ReDim EmployeeDates(0) 'create initial
'loop through all the cells
For i = 1 To Dates.Count
If (EmployeeRange(i) = Employee) Then 'if the employee is equal to the employee in that range
If (Not (IsInArray(Dates(i), EmployeeDates))) Then 'if the date hasn't already been counted
EmployeeDates(UBound(EmployeeDates)) = Dates(i) 'add the date
ReDim Preserve EmployeeDates(UBound(EmployeeDates) + 1) 'add another element to the array for the next one
End If
End If
Next i
WorkingDaysCount = UBound(EmployeeDates) 'since ubound is 0-based this is the total count of dates
End Function
Private Function IsInArray(valToBeFound As Variant, arr As Variant) As Boolean
'DEVELOPER: Ryan Wells (wellsr.com)
'DESCRIPTION: Function to check if a value is in an array of values
'INPUT: Pass the function a value to search for and an array of values of any data type.
'OUTPUT: True if is in array, false otherwise
Dim element As Variant
On Error GoTo IsInArrayError: 'array is empty
For Each element In arr
If element = valToBeFound Then
IsInArray = True
Exit Function
End If
Next element
Exit Function
IsInArrayError:
On Error GoTo 0
IsInArray = False
End Function

Converting input range to array in function

I have been learning VBA and thought I was getting the hang of it, but for some reason this basic task is eluding me. I am trying to create a function where I select an input region of numbers (really only one row or column), and then output the summation of the numbers in a cell. Here is my code:
Function CashFlow(CashArray As Excel.Range)
Dim cashflows() As Variant
cashflows = CashArray.Value
amount = CashArray.Rows.Count
dim y()
redim y(amount)
Sum = 0
For i = 1 To amount
y(i) = cashflows(i)
Sum = Sum + y(i)
Next i
CashFlow = Sum
End Function
Despite me knowing how to do essentially this in a subroutine, the fact that I'm getting my data from the function's input is throwing me off. How do I accomplish this task?
Just to summarize the above comments, please give the following code a try:
Option Explicit
Function CashFlow(CashArray As Excel.Range)
Dim sum As Double
Dim x As Long, y As Long
Dim cashflows() As Variant
cashflows = CashArray.Value
sum = 0
For y = LBound(cashflows, 1) To UBound(cashflows, 1)
For x = LBound(cashflows, 2) To UBound(cashflows, 2)
sum = sum + cashflows(y, x)
Next x
Next y
CashFlow = sum
End Function
Note, that this code summarizes all cells in the given range (even if there are multiple rows and columns in that range). Basically, the range is stored in the array cashflows. As mentioned in the comments, this will yield a two-dimensional array. The function LBound(cashflows) will give you the Lower Boundary of that array.
By specifying LBound(cashflows, 1) I am specifically asking for the lower boundary of the first dimension (in this case the rows). UBound returns the Upper Boundary of that array for the specified dimension. Similarily UBound(cashflows, 2) returns the last column (second dimension) of the given array.
Remark: I am always using LBound() and UBound() in my code instead of starting with 0 or 1 to avoid coding errors. Some people prefer Option Base 0 and some prefer Option Base 1. While neither will have an impact on the above (as ranges always yield an array starting with 1) it is just good coding practice to prevent bugs in the future.

Averaging numerous values in a class

I have a class with a lot of parameters as it takes in all the columns in Excel, since this is the first step I usually do when building a macro. Then I started to think about how to actually accomplish what I'm trying to do and I think I just wasted a couple of hours of coding.
What I am trying to do is average each of the parameters inside the class as well as trying to get minimum and maximum values. There are about 100000 rows in these excel files.
My plan was to put everything into a collection and then I was thinking there was going to be some sort of averaging function or a min and max function for the collection parameter, or a way to send an array to a function that can calculate min or max.
What would be ideal:
Dim collectionOfRecords as new collection
for each row in sheet.rows
Dim r as New RmmRecord
call r.PopulateClass(row)
collectionOfRecords.add r
next row
Then get the average from the collection somehow like this:
dim parameter1Average as double
parameter1Average = collectionOfRecords.Parameter1.Average '(ha, I wish)
OR:
parameter1average = GetAverageFromCollection(collectionOfRecords, Parameter1)
Public Function GetAverageFromCollection(records as Collection, parameterToAverage as something??)
for each record in records
sum = record.parameterToAverage + sum
next record
GetAverageFromCollection = sum / records.count
end function
Thank you!

Function to order numbers; find common difference and gap

I have a vb6 function that will take up to 7 numbers, order them, find a common difference. There will be a gap in this sequence of numbers. I also want to identify the missing number.
Example input is 19,17,20,and 16. The output should be an array 16,17,18,19,20 in this order. Any help? I may be able to interpret vb.net code to vb6, but vb6 is preferred to me.
Here’s an outline of what needs to be done:
Find the smallest and largest number
Create an array with enough entries to hold the full range from the smallest to largest
Fill the array with the numbers from lowest to highest.
Note that you don’t need to sort the numbers at all. You just need to find the extreme values.
Here’s a code outline in VB6:
Function RangeFrom(ParamArray Numbers() As Long) As Long()
Dim Lowest As Long
Dim Highest As Long
Lowest = Numbers(0)
Highest = Numbers(0)
Dim Number As Long
For Each Number In Numbers
If Number < Lowest Then Lowest = Number
If Number > Highest Then Highest = Number
Next
Dim Result(0 To Highest - Lowest) As Long
Dim I As Long
For I = 0 To Highest - Lowest
Result(I) = Lowest + I
Next
FromRange = Result
End Function
Since you have tagged VB.NET as well, this should be convertible to VB6(i've avoided Linq):
Public Shared Function FillGaps(input As Int32()) As Int32()
Dim output = New List(Of Int32)
Array.Sort(input)
' now we'll find the min/max-values at the first/last indices
For i As Int32 = input(0) To input(input.Length - 1)
output.Add(i)
Next
Return output.ToArray()
End Function
Use it in the following way :
Dim intArray = {19, 17, 20, 16}
intArray = FillGaps(intArray)
Note that this approach skips duplicates.