I have a for loop that iterates through each sheet in my workbook. Each sheet is identical for most purposes. I am making an overview sheet in my workbook to express the results from the data in the other sheets(the ones that are identical).
There are 11 vehicles, each with their own sheet that has data from a test from each day. On any given day there can be no tests, or all the way to 30,000 tests. The header of each column in row 47 states the date in a "06/01/2021 ... 06/30/2021" format. The data from each iteration of the test will be pasted in the column of the correct date starting at row 49.
So what my overview page needs to display is the data from the previous day. In one cell on my overview there is a formula for obtaining just the number of the day of the month like 20 or 1 etc. Using this number, the number of the day is the same as the column index that the previous day's data will be in conveniently. So in my for loop I want to have a table that has the name of each sheet in column B, in column C I want to display the total number of tests done in that day(not the sum of all the data), in column D I need the number of tests with a result of 0, in column E I need the number of tests that have a result above the upper tolerance, and in column F I need the number of tests that have a result below the lower tolerance minus the result in column D.
I've been playing with the Application.WorksheetFunction.Count and CountIf functions but I keep getting 0 for every single cell. I made two arrays for the upper and lower tolerance values which are type Longs called UTol and LTol respectively. TabList is a public array that has each of the sheet names for the vehicles stored as strings. finddate is an integer that reads in Today's day number which is yesterday's column index. I've included a picture of the table in question and my for loop code:
finddate = Worksheets("Overview").Range("A18").Value
For TabNRow = 1 To 11
startcol = 3
Worksheets(TabList(TabNRow)).Activate
Debug.Print (TabList(TabNRow))
'Total number of tests done that day
Worksheets("Overview").Cells(startrow, startcol).Value = Application.WorksheetFunction.Count(Range(Cells(49, finddate), Cells(30000, finddate)))
startcol = 4
'Total number of 0 results, this used to get the number of 0's from each sheet but that row has since been deleted
Worksheets("Overview").Cells(startrow, startcol).Value = Worksheets(TabList(TabNRow)).Cells(48, finddate).Value
startcol = 5
'Total number of results above tolerance
Worksheets("Overview").Cells(startrow, startcol).Value = Application.WorksheetFunction.CountIf(Range(Cells(49, finddate), Cells(30000, finddate)), ">" & UTol(TabNRow))
startcol = 6
'Total number of results below tolerance
Worksheets("Overview").Cells(startrow, startcol).Value = Application.WorksheetFunction.CountIf(Range(Cells(49, finddate), Cells(30000, finddate)), "<" & LTol(TabNRow))
startrow = startrow + 1
Next TabNRow
```
No need to select/activate sheets when referencing them. See: How to avoid using Select in Excel VBA
Something like this should work:
Dim wsOv As Worksheet, wsData As Worksheet, rngData As Range
Set wsOv = ThisWorkbook.Worksheets("Overview")
finddate = wsOv.Range("A18").Value
For TabNRow = 1 To 11
Set wsData = ThisWorkbook.Worksheets(TabList(TabNRow))
Set rngData = wsData.Range(wsData.Cells(49, finddate), _
wsData.Cells(Rows.Count, finddate).End(xlUp))
Debug.Print wsData.Name, rngData.Address 'edited
With wsOv.Rows(2 + TabNRow)
.Columns("C").Value = Application.Count(rngData)
.Columns("D").Value = Application.CountIf(rngData, 0)
.Columns("E").Value = Application.CountIf(rngData, ">" & UTol(TabNRow))
.Columns("F").Value = Application.CountIf(rngData, "<" & LTol(TabNRow)) _
- .Columns("D").Value
End With
Next TabNRow
Related
I would like to draw automatically accumulated inventory plot using Excel VBA 2016.
I have inventory data for different year in the format of "20XXYY". Here "XX" indicates year and "YY" indicates week number in a year (1 to 52). I will have data similar to picture 1. Then I would like to sort it as picture 2. Finally draw plot as picture 3. However, I would like to have it automatically using VBA.
My question is that how can I create sequential YearWeek column automatically for the plot?
I have used "=SUM($Y$2:Y2)"^^ formula in "Accumulated Inventory" column. I will highly appreciate if I will get any clue.
'This is the answer. I have figure it out. Thanks for your support.
Sub consecutiveNumber()
Dim wsFrom As Worksheet
Dim Com As String
Dim lLastNumber,FirstNumber, l, i As Long
'change to names of sheets you are coptying from and to
Set wsFrom = ThisWorkbook.Sheets("Sheet1")
'Get the value in the last and first used text box of column A
lLastNumber = wsFrom.Range("A" & Rows.Count).End(xlUp).Value
FirstNumber = wsFrom.Range("A1").Value
'clear column B
wsFrom.Range("B:B").Clear
'Initializing the cell index value
i = 1
'fill column B on sheet 1 with first to last number on column A
For l = FirstNumber To lLastNumber
'Converting number to string
Com = Right(CStr(l), 2)
If CInt(Com) >= 53 Then
l = l + 48
wsFrom.Range("M" & i) = l
i = i + 1
Else
wsFrom.Range("M" & i) = l
i = i + 1
End If
Next
End Sub
I want to put a Sum formula in a row of a table, in columns C to H, but the code I’ve come up with somehow doesn’t work. The situation is as follows:
the number of the 1st row of the table varies (the 1st column is
always B)
the number of the 1st row in the formula varies, but is always the 3rd row of the table
the number of the row that should contain the formula varies, but in the macro I calculate that number relative to the 1st row of the table
the number of the last row in the formula varies, but is always 1 less than the number of the row that should contain the formula
To be more specific and hopefully more clear, let’s say that:
the number of the first row of the table = startnum
then the number of the 1st row in the formula = startnum+3
the number of the row that should contain the formula = startnum+x
then the number of the last row in the formula = startnum+x-1
Trying to find out what my code could be, I recorded a macro. Based on that I have tested the following code:
With Worksheets("A&N")
.Range("C16:H16").Formula = "=SUM(C7:C15)"
End With
This works fine, but as I’ve described, the numbers 16, 7 and 15 are actually variable.
I’ve tried to translate this code to my situation, and made this code:
Set rngOpmaak = Range(rngTabel.Cells(startnum + x, 2), rngTabel.Cells(startnum + x, 7))
rngOpmaak.Formula = "=SUM(“C” & startnum + 3 & “:C” & startnum + x -1)"
When I run the macro I get the message that the second line can’t be compiled. I’ve seen solutions on this site that to me look exactly like my code, so I don’t understand what’s wrong with mine.
I’ve also tried:
rngOpmaak.FormulaR1C1 = "=SUM(R" & startnum + 3 & "C:R" & startnum + x -1 & "C)"
But with startnum=2 (1st row of the table) the formula becomes =SUM(C$3:C$5) to =SUM(H$3:H$5) (without the quotations) instead of =SUM(C4:C6) to =SUM(H4:H6).
Can anyone help me with what the line of code should be? All suggestions are much appreciated.
You'll need to adjust this to your situation, as I didn't use the same variables you did (preferring more expressive ones):
Sub test()
Dim output As Range, dataStart As Integer, dataEnd As Integer, rowsOfData As Integer, nCols As Integer
' Row 1: Table headers
' Row 2: 1st row of data
' Row 3: more data
' Row 4: more data
' Row 5: last row of data
' Row 6: Summary row (location of formula)
dataStart = 2
dataEnd = 5
rowsOfData = 1 + dataEnd - dataStart
nCols = 4
Set output = Range(Cells(dataEnd + 1, 1), Cells(dataEnd + 1, nCols))
output.FormulaR1C1 = "=SUM(R[-" & rowsOfData & "]C:R[-1]C)"
End Sub
The generated formula uses relative references, as it was given offsets ([]) from the current cell, rather than absolute R/C values, e.g.
R2C:R5C -> A$2:A$5 no matter where in column A the formula is entered
R[-4]C:R[-1] -> A2:A5 if the formula is entered in A6.
Right - this is a tricky one to phrase so I'm going to use a couple of images to help me.
In Columns A and B is a varying list of team names and the number of players each team has.
Column D contains the desired output.
I need a formula, to be inserted into Cell D2 and dragged down as far as the total of Column B, to return the team names - but crucially to allow a number of rows beneath which return blank. The number of blank rows beneath is effectively equal to 1 - the number of players in that team.
I have given it some thought, but can't come up with a suitable formula. Any ideas?
Also suggestions for a better title are welcome.
The following VBA function will do exactly what you want. Let me know if any part of it is not clear to you.
Sub teamRows()
Dim colDRowNumber As Integer
Dim i As Integer
Dim teamName As String
Dim numberOfRows As Integer
Dim HowFar As Integer
' Loop through the teams in column A
HowFar = Application.WorksheetFunction.CountA(Range("A:A"))
' Variable to keep count of rows in column D
colDRowNumber = 2
For i = 2 To HowFar
' Get the team's name and number of rows
teamName = Range("A" & i).Value
numberOfRows = Range("B" & i).Value
' Fill in the team's name in column D
Range("D" & colDRowNumber).Value = teamName
' Increase the row number by the number of empty rows required
colDRowNumber = colDRowNumber + numberOfRows
Next i
End Sub
A complex but short attempt - I wanted to avoid loops.
Example below works on A2 to A20
y = Split(Join(Application.Transpose(Application.Evaluate("=index(substitute(substitute(substitute(REPT(A2:A20 &"","",B2:B20),A2:A20&"","",""X"",1),A2:A20,""""),""X"",A2:a20),0,1)")), ","), ",")
[d2].Resize(UBound(y)) = Application.Transpose(y)
I made a function that counts the number of items in a given month.
Column A is the month, and Column B is the number of items in that month.
Cell B1 has:
=countItems(A1)
Excel data:
Code:
Function countItems(month)
Application.Volatile
Dim count As Integer
If Not (month = 0) Then
count = 0
Do While Not (Cells(month.row + count + 1, 3) = 0)
count = count + 1
Loop
countItems = count
Else
countItems = ""
End If
End Function
I dragged the formula down from B1 to B500 and it works properly for every month. The formula returns nothing if there is no month in the corresponding A cell.
I have multiple sheets using the same formula on similarly-structured data sets.
Whenever the values in column B update for this Sheet 1, the other sheets will change too. However, Sheet 2 will update using Column C from Sheet 1.
If I have Sheet 2 recalculate, Sheet 1 will update using Column C from Sheet 2.
The function counts the number of items in a given month by checking how far down it can read in Column C before it finds the blank cell, indicating that the month is over.
Sheet 2 has 1 item in the first month, but it will still return 3 due to Sheet 1 having 3 items (counts Row 1 through 3 and stops at Row 4).
The second month of Sheet 2 begins on Row 3. But since the function is reading Column C from Sheet 1, it will run into the blank cell after counting 1 more item (counts Row 3 and stops at Row 4). Therefore no matter how many items are in Sheet 2 Month 2, it will return 1.
The function always uses the correct Column A, and only displays a number in Column B where there is date in Column A.
The consequence is that only 1 sheet can have the correct values, and doing that disrupts the other sheets.
I cannot solve this at the moment because I am new to VBA.
I have thought of making all of the function's cell references include a self-reference to the current cell's sheet, but I don't know how to do that and I don't know if it would work.
Edit: I couldn't make it work this way, but Application.Caller.Offset() with relative cell position worked as a solution. I am still wondering if there is a way to use absolute cell position though.
The sheets are not grouped together.
it's because there's a "time-space shift" between the range passed to the function and the range "felt" as the "caller" one by the function
you can see this behavior by modifying the function code as follows
Function countItems(month)
Application.Volatile
Dim count As Integer
Dim r As Range
Dim p As Variant, a As Variant
Set r = Application.Caller '<~~ retrieve the actual "calling cell" of the current function "instance"
p = r.Parent.Name
a = r.Address
MsgBox p & " vs " & month.Parent.Name & vbCrLf & a & " vs " & month.Address '<~~ compare the function "calling cell" vs the "passed cell"
If Not (month = 0) Then
count = 0
Do While Not (Cells(month.Row + count + 1, 3) = 0)
count = count + 1
Loop
countItems = count
Else
countItems = ""
End If
End Function
and you'll see msgboxs prompts showing you differences between the function "calling cell" and "passed cell" addresses and/or sheets
so to avoid this behavior you could rely on the "calling range only", like follows:
Option Explicit
Function countItems(month)
Application.Volatile
Dim r As Range
Set r = Application.Caller '<~~ retrieve the actual "calling cell" of the current function "instance"
'the "calling cell" is the one with the called function in its formula, i.e. in column "B" as per your data structure. then ...
If Not IsEmpty(r.Offset(, -1)) Then '<~~ ... the column with dates are one column to the left, and ...
Do While Not IsEmpty(r.Offset(countItems + 1, 1)) '<~~ ... the values to be checked for are one column to the right
countItems = countItems + 1
Loop
End If
End Function
I have a puzzle I've been trying to solve for ages now, but it's quite simply beyond me.
I have a spreadsheet with 3 columns. Column A is instructor ID numbers, Column B is their course Start date and Column C is their course end date. There are multiple courses for each instructor ID.
I'm basically trying to answer the question, what is the maximum number of courses this instructor is teaching at any given time.
Essentially, I need to find, for each ID number, the number of maximum, concurrent, overlapping date ranges.
The trouble is, while I know how to find overlapping date ranges, I don't know how to count the number of concurrent courses.
Eg.
Instructor 115 has the following date ranges listed:
9/10/13 / 11/04/13
9/17/13 / 11/11/13
11/05/13 / 12/30/13
11/12/13 / 1/20/14
While the 11/05/13 course overlaps with both the 9/17/13 course and the 11/12/13 course, they do not overlap with each other... so this instructor is only teaching a maximum of 2 courses at any time.
Is there a way to write a function that will return the highest number of concurrent overlapping date ranges for each ID?
Edit not form OP to transfer details from a comment:
I can solve this geometrically, but I don't know how to do that in a VBA function (I'm still very new to programming). If I were to solve this outside of code, I would create a table for each ID making a column for every day. I'd then create a row for each date range, marking a 1 in each column that range overlaps with. then I’d sum the total overlaps for each day. Then I’d use a simple MAX function to return the highest number of consecutive overlaps. Is there a way to do this inside of a function without having Excel physically draw out these tables?
Using VBA, assuming Column A contains your start dates, and column B contains your end dates, and assuming your data starts in row 1 and there are no blank rows in your data, the below sub will do what you outlined in your comment:
Sub getMaxConcurrent()
'get minimum date (startDate)
Dim startDateRange
Set startDateRange = Range("A1", Range("A1").End(xlDown))
Dim startDate As Date
startDate = WorksheetFunction.Min(startDateRange)
'get maximum date (endDate)
Dim endDateRange
Set endDateRange = Range("B1", Range("B1").End(xlDown))
Dim endDate As Date
endDate = WorksheetFunction.Max(endDateRange)
'get date range (dateInterval)
Dim dateInterval As Integer
dateInterval = DateDiff("d", startDate, endDate)
'Create daily table header
Rows("1:1").Insert Shift:=xlDown, CopyOrigin:=xlFormatFromLeftOrAbove
Dim x As Integer
For x = 0 To dateInterval
Dim dateVal As Date
dateVal = DateAdd("d", startDate, x)
Cells(1, 3 + x).Value = dateVal
Next
'Fill in daily table
Dim y As Integer
y = 2
Dim startDateValue As Date
startDateValue = Cells(y, 1).Value
Do Until IsEmpty(Cells(y, 1).Value)
For x = 3 To dateInterval + 3
If (Cells(y, 1).Value <= Cells(1, x).Value) Then
If (Cells(y, 2).Value >= Cells(1, x).Value) Then
Cells(y, x).Value = 1
Else
Cells(y, x).Value = 0
End If
Else
Cells(y, x).Value = 0
End If
Next
y = y + 1
Loop
'sum up each day
For x = 3 To dateInterval + 3
Cells(y, x).Value = WorksheetFunction.Sum(Range(Cells(2, x).Address & ":" & Cells(y - 1, x).Address))
Next
MsgBox ("Max concurrent courses: " & WorksheetFunction.Max(Range(Cells(y, 3).Address & ":" & Cells(y, x).Address)))
End Sub
If you have data down to row 1000 then this "array formula" will give the maximum number of concurrent courses for an Instructor ID in E2
=MAX(COUNTIFS(A:A,E2,B:B,"<="&B$2:C$1000,C:C,">="&B$2:C$1000))
confirmed with CTRL+SHIFT+ENTER
Let's assume there is only one instructor and you have start and end dates in A1:B4.
Copy A1:A4 to A7:A10, copy B1:b4 to A11:a14 (right under it). Select A7:A14, hit Sort (on data tab) and "remove duplicates". You have a list unique list of dates in ascending order. Let's assume there were no duplicates (as in your example), your of date is same A7:a14. Select it copy, and paste spacial with transpose to C5.
At this point You have start and end dates in A1:B4 and list of uniqe dates in C5:J5. Put formula =IF(AND($A1<=C$5,C$5<=$B1),1,0) in C1 and copy it to C1:J4.
put formula =SUM(C1:C4) in C6 and copy it to C6:J6.
Maximum number in C6:j6 is your maximum concurrent courses for this instructor