Speed up Loops in VBA - excel

I'm currently using 2 if loops and a do loop to get my result. But this is proving to be extremely slow and usually crashing. The file is around 16MB. Secondly, it is not correctly changing the start date in Sheet 1 for all dates i.e. sometimes if the date is less than the Startofweek. It will still give the initial date.
In sheet "Leasing". Column A contains Car numbers, Column E contains the Start Date and Column F contains the End Date.
Sheet 1 will be where the results are needed. Column A the start date, B the end Date and C the Car number.
I need it to give me the car number in Sheet 1:
i) If car number is not blank then
if the end date in Leasing is blank.
a) End date in Sheet 1 would be Endofweek(inputted by user)
b) Start date in Sheet 1would be Startofweek(inputted by user) or Start Date(Leasing). Whichever is greater.
if end date is less than Endofweek but greater than startofweek.
a) End date in Sheet 1 would be EndDate leasing
b) Start date in Sheet 1would be Startofweek(inputted by user) or Start Date(Leasing). Whichever is greater.
Column D of Sheet 1 then contains the difference between End of week and Start of week(Column B - Column A)
Example: Startofweek: 22/3/21, Endofweek: 28/3/21
'''
Sub active_leasing_cars()
Application.ScreenUpdating = False
Const Leasing_start = 5 'Column leasing start from Leasing Hisaab
Const Leasing_end = 6 'Column Leasing start from leasing Hisaab
Const Leasing_car = 1
Const TARGET = "Sheet1"
Const LEASING = "Leasing Hisaab"
Dim wb As Workbook, ws As Worksheet, wsResult, wsLeasing As Worksheet
Dim i, j, iLastrow As Integer
Dim Endofweek, Startofweek As String
Set wb = ThisWorkbook
Set wsResult = wb.Sheets(TARGET)
Set wsLeasing = wb.Sheets(LEASING)
iLastrow = wsLeasing.Cells(Rows.Count, Leasing_car).End(xlUp).Row
i = 1
j = 2
Endofweek = InputBox("Please enter end date", "Enter Date", Format(Date, "dd/mm/yyyy"))
Startofweek = InputBox("Please enter start date ", "Enter Date", Format(Date, "dd/mm/yyyy"))
Do
i = i + 1
If wsLeasing.Cells(i, Leasing_car) = "" Then
If wsLeasing.Cells(i, Leasing_end) = "" Then
wsResult.Cells(j, 2) = Endofweek
If wsLeasing.Cells(i, Leasing_start) <= Startofweek Then
wsResult.Cells(j, 1).Value = Startofweek
Else: wsResult.Cells(j, 1) = wsLeasing.Cells(i, Leasing_start)
End If
ElseIf wsLeasing.Cells(i, Leasing_end) >= Startofweek And wsLeasing.Cells(i, Leasing_end) <= Endofweek Then
wsResult.Cells(j, 2) = wsLeasing.Cells(i, Leasing_end)
If wsLeasing.Cells(i, Leasing_start) <= Startofweek Then
wsResult.Cells(j, 1) = Startofweek
Else: wsResult.Cells(j, 1) = wsLeasing.Cells(i, Leasing_start)
End If
End If
wsResult.Cells(j, 3) = wsLeasing.Cells(i, Leasing_car)
wsResult.Cells(j, 4) = DateDiff("d", wsResult.Cells(j, 1), wsResult.Cells(j, 2))
j = j + 1
End If
Loop While i < iLastrow
Application.ScreenUpdating = True
End Sub

That shouldn't take too long. You've no formulae in your worksheets do you?
You could disable display updates and the automatic calculations via the below at the start of your macro, and see how you go:
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
(don't forget to reenable at the end)
If that doesn't help, then a bit more work is required - I'd advise loading your data into a 2D array though and going from there.
Dim leasingData()
iRows = 0
Do Until isEmpty(Sheets("Leasing").cell(2+iRows,1))
redim preserve leasingData(2, iRows)
leasingData(0, iRows) = Sheets("Leasing").Cells(2 + iRows, 1)
leasingData(1, iRows) = Sheets("Leasing").Cells(2 + iRows, 5)
leasingData(2, iRows) = Sheets("Leasing").Cells(2 + iRows, 6)
iRows = iRows = 1
Loop
Then from there, your searching and indexing should be much faster. The slowest part of VBA is interacting with the worksheets.
for iCar = 0 to ubound(leasingData,2)
if leasingData(2, iCar) = "" then
'No end date specified for this car, sub in end of week
end if
if leasingData(1, iCar) <= Start of week then
'The car leaase started before the start of this week, so update to start of week
end if
next iCar
etc etc
Then you can push the data out of the array onto your result sheet:
for iCar = 0 to ubound(leasingData,2)
Sheets("Target").cells(1+iCar, 3) = leasingData(0, iCar)
Sheets("Target").cells(1+iCar, 1) = leasingData(1, iCar)
Sheets("Target").cells(1+iCar, 2) = leasingData(2, iCar)
next iCar
In terms of your end of week update problem:
ElseIf wsLeasing.Cells(i, Leasing_end) >= Startofweek And wsLeasing.Cells(i, Leasing_end) <= Endofweek Then
wsResult.Cells(j, 2) = wsLeasing.Cells(i, Leasing_end)
Will never do what you want it to, you need to replace the RHS with Endofweek

You date comparison does not work properly because you have declared Startofweek As String. As a result wsLeasing.Cells(i, Leasing_start) <= StartOfWeek is False when the date 28/07/2020 is compared to the string 22/03/2021.
Option Explicit
Sub active_leasing_cars()
Const Leasing_start = 5 'Column leasing start from Leasing Hisaab
Const Leasing_end = 6 'Column Leasing start from leasing Hisaab
Const Leasing_car = 1
Const TARGET = "Sheet1"
Const LEASING = "Leasing Hisaab"
Dim wb As Workbook, ws As Worksheet, wsResult, wsLeasing As Worksheet
Dim i As Long, j As Long, iLastrow As Long
Dim EndOfWeek As Date, StartOfWeek As Date, s As String
Dim ar, t0 As Single: t0 = Timer
Set wb = ThisWorkbook
Set wsResult = wb.Sheets(TARGET)
Set wsLeasing = wb.Sheets(LEASING)
iLastrow = wsLeasing.Cells(Rows.Count, Leasing_car).End(xlUp).Row
' end of week
s = InputBox("Please enter end date dd/mm/yyyy", "Enter END Date", Format(Date, "dd/mm/yyyy"))
If IsDate(s) Then
ar = Split(s, "/")
EndOfWeek = DateSerial(ar(2), ar(1), ar(0))
Else
MsgBox s & " is not a date", vbCritical
Exit Sub
End If
' start of week
s = InputBox("Please enter START date ", "Enter Start Date dd/mm/yyyy", Format(EndOfWeek - 6, "dd/mm/yyyy"))
If IsDate(s) Then
ar = Split(s, "/")
StartOfWeek = DateSerial(ar(2), ar(1), ar(0))
Else
MsgBox s & " is not a date)", vbCritical
Exit Sub
End If
' sanity check
If StartOfWeek > EndOfWeek Then
MsgBox "Start: " & StartOfWeek & " after End: " & EndOfWeek, vbCritical
Exit Sub
End If
Dim sCar As String, dtStart As Date, dtEnd As Date, val As Variant
j = 1 ' sheet1 row
Application.ScreenUpdating = False
For i = 2 To iLastrow
sCar = wsLeasing.Cells(i, Leasing_car).Value
If Len(sCar) > 0 Then
' check start date
val = wsLeasing.Cells(i, Leasing_start).Value
If Len(val) > 0 Then
dtStart = val
Else
MsgBox "Start date is blank", vbCritical, "Row " & i
End If
' End Dates
val = wsLeasing.Cells(i, Leasing_end).Value
If Len(val) = 0 Then
'if the end date in Leasing is blank.
'a) End date in Sheet 1 would be Endofweek(inputted by user)
dtEnd = EndOfWeek
Else
dtEnd = val
End If
' filter for dates
If dtEnd < StartOfWeek Or dtStart > EndOfWeek Then
' ignore as either already ended or not started yet
Else
'b) Start date in Sheet 1 would be Startofweek(inputted by user)
' or Start Date(Leasing). Whichever is greater.
If dtStart > StartOfWeek Then
wsResult.Cells(j, 1) = dtStart
Else
wsResult.Cells(j, 1) = StartOfWeek
End If
wsResult.Cells(j, 2) = dtEnd
wsResult.Cells(j, 3) = sCar
wsResult.Cells(j, 4).FormulaR1C1 = "=RC[-2]-RC[-3]"
j = j + 1
End If
End If
Next
wsResult.Columns("B:C").NumberFormat = "dd/mm/yyyy"
Application.ScreenUpdating = True
MsgBox j - 1 & " records found for " & StartOfWeek & " to " & EndOfWeek, _
vbInformation, Int(Timer - t0) & " seconds"
End Sub

Related

Issue making Calendar

I got problem with calendar in VBA. Wants to create a calendar that will show/paint the range of week numbers from 2022 depending on the date entered in columns A22 and B22. The problem occurs when the week numbers repeat between months.
Tydzien = Week
Sty = January
Lut = February
Option Explicit
Sub Kolorowaniedaty()
Dim rok As Integer
rok = Left(Cells(22, 2), 4)
Dim miesiacpocz As Integer
miesiacpocz = Mid(Cells(22, 2), 7, 1)
Dim miesiackon As Integer
miesiackon = Mid(Cells(22, 3), 7, 1)
Dim Datapocz As Integer
Datapocz = Application.WorksheetFunction.WeekNum(Cells(22, 2), 2)
Dim Datakon As Integer
Datakon = Application.WorksheetFunction.WeekNum(Cells(22, 3), 2)
Dim Rokzdaty As String
Rokzdaty = CStr(Mid(Cells(22, 2), 3, 2))
Dim Rok2022 As Byte
Rok2022 = 22
Dim kolumna As Byte
For kolumna = 1 To 20
If Rokzdaty = Rok2022 And miesiacpocz = miesiackon Then
Range(Cells(22, Datapocz + 4), Cells(22, Datakon + 4)).Interior.Color = vbYellow
Else: Range(Cells(22, Datapocz + 4), Cells(22, Datakon + 5)).Interior.Color = vbYellow
End If
Next kolumna
End Sub
I cant upload image of Makro and Calendar from excel cuz i dont have enought points of reputations. If someone can help from private chat i will be really really thankful. Its must have from to my work.
[![enter image description here][3]][3]
Its Its suppose to mark 11 weeks but its show only 10 weeks. Any advice?
[3]: https://i.stack.imgur.com/X8kwQ.png
Iterate over each day in the date range and increment the column number each monday or change of month. Store the column numbers in an array and use it as a lookup to determine the column number for a given date. Run this is a new clean workbook.
update - complete rewrite
Option Explicit
Const START_COL = 4
Const START_ROW = 22
Const MAX_YEARS = 4
Const START_YEAR = 2022
Sub CalendarDemo()
Dim ws As Worksheet
Dim dt As Date, dtDay1 As Date
Dim wkno As Long, dayno As Long
Dim colno As Long, i As Long, c As Long, r As Long
Dim arCol, arDate
ReDim arCol(1 To 2, 1 To MAX_YEARS * 12 * 7)
ReDim arDate(1 To MAX_YEARS * 366, 1 To 5) ' wkno, month no, column, date, dow
' start Jan 1
dtDay1 = DateSerial(START_YEAR, 1, 1)
colno = 1
wkno = 1
i = 1
' iterate through days built look up array
dt = dtDay1
Do While Year(dt) < START_YEAR + MAX_YEARS
arDate(i, 2) = Month(dt)
arDate(i, 5) = Weekday(dt, vbMonday)
If i > 1 Then
' change of week or month
If arDate(i, 5) = 1 Then
wkno = wkno + 1
If (wkno > 52) And (Month(dt) = 1) Then wkno = 1
colno = colno + 1
ElseIf arDate(i, 2) <> arDate(i - 1, 2) Then
colno = colno + 1
End If
End If
' reset wkno to 1 on jan 1st
If wkno >= 52 And arDate(i, 2) = 1 Then wkno = 1
arDate(i, 1) = wkno
arDate(i, 3) = colno
arDate(i, 4) = dt
' fill arCol
arCol(1, colno) = Format(dt, "mmm yyyy")
arCol(2, colno) = wkno
dt = dt + 1
i = i + 1
Loop
' paint cells
Dim lastrow As Long, dtStart As Date, dtEnd As Date
Dim colStart As Long, colEnd As Long, n As Long, m As Long
Set ws = Sheets(1)
Call testdata(ws)
With ws
lastrow = .Cells(.Rows.Count, "B").End(xlUp).Row
For r = START_ROW To lastrow
' check dates are valid
dtStart = .Cells(r, "B")
dtEnd = .Cells(r, "C")
If dtEnd < dtStart Then
MsgBox "End Date before Start Date on row " & r, vbCritical
Exit Sub
ElseIf dtStart < dtDay1 Then
MsgBox "Start Date before 1 Jan " & START_YEAR & " on row " & r, vbCritical
Exit Sub
End If
' calc day number relative to day1
m = DateDiff("d", dtDay1, dtStart, dtDay1) + 1
n = DateDiff("d", dtDay1, dtEnd, dtDay1) + 1
If n > UBound(arDate) Or m > UBound(arDate) Then
MsgBox "Increase MAX_YEARS for row " & r, vbCritical
Exit Sub
End If
' lookup col number
colStart = arDate(m, 3) + START_COL
colEnd = arDate(n, 3) + START_COL
' merge and color
With .Cells(r, colStart)
With .Resize(1, colEnd - colStart + 1)
.Interior.Color = vbYellow
.Borders.LineStyle = xlContinuous
.Merge
End With
.Value = Space(5) & Format(dtStart, "dd mmm") & " - " & Format(dtEnd, "dd mmm yyyy")
End With
Next
End With
' add headers
Call FormatSheet(ws, arCol, arDate, colno)
MsgBox "Generated " & colno & " Columns", vbInformation
End Sub
Sub FormatSheet(ws As Worksheet, arCol, arDate, colno As Long)
Dim c As Long, i As Long, n As Long, dt As Date
' format sheet header rows
With Sheet1
.Rows("10:21").Clear
.Cells.MergeCells = False
With .Range("E20").Resize(2, colno)
.NumberFormat = "#"
.HorizontalAlignment = xlCenter
.Value2 = arCol
End With
' merge months
i = 0
For c = 5 To colno + 4
If .Cells(20, c + 1) = .Cells(20, c) Then
i = i + 1
Else
With .Cells(20, c - i)
Application.DisplayAlerts = False
.Resize(1, i + 1).Merge
Application.DisplayAlerts = True
.Resize(2, 1).Borders(xlLeft).LineStyle = xlContinuous
End With
i = 0
End If
Next
End With
' calendar to check array
For i = 1 To UBound(arDate)
dt = arDate(i, 4) ' date
n = arDate(i, 5) ' weekday
If dt > 0 Then
n = Weekday(dt, vbMonday)
ws.Cells(10 + n, arDate(i, 3) + START_COL) = Day(dt)
End If
' mon,tue,wed
If i < 8 Then
ws.Cells(10 + n, START_COL) = WeekdayName(n)
End If
Next
End Sub
Sub testdata(ws)
With ws
.Cells(22, 2) = "2022-01-01": .Cells(22, 3) = "2022-03-08"
.Cells(23, 2) = "2022-02-01": .Cells(23, 3) = "2022-02-28"
.Cells(24, 2) = "2022-03-01": .Cells(24, 3) = "2022-03-31"
.Cells(25, 2) = "2022-03-15": .Cells(25, 3) = "2022-05-15"
.Cells(26, 2) = "2022-03-15": .Cells(26, 3) = "2024-03-20"
End With
End Sub

copy paste by dates from one worksheet to anoter

I have days in a month sorted form Cell A to AH (eg: 1.1.2021 is A, 2.1.2021 is B and so on), and I need to copy those values to another worksheet. My code works, but is too long for all 31 days (error: function is too large). Is there any way to optimize it or sort it by arrays? for the other days its identical code except the part from where it gets values now it gets from "jan" worksheet cells "C", if it's 2nd day it should get values from Cells "D" eg:
1st day of month:Worksheets("List1").Range("I16").Value = Worksheets("Jan").Range("C10").Value;
2nd day of month: Worksheets("List1").Range("I16").Value = Worksheets("Jan").Range("D10").Value
code looks like this:
Function TEST()
Dim Day, Month As Variant
Day = Range("V6").Value
Month = Range("V5").Value
If Month = 1 Then
Worksheets("List1").Range("I16").Value = Worksheets("Jan").Range("C10").Value
Worksheets("List1").Range("N6").Value = Worksheets("Jan").Range("C18").Value
Worksheets("List1").Range("T5").Value = Worksheets("Jan").Range("C12").Value
Worksheets("List1").Range("T6").Value = Worksheets("Jan").Range("C11").Value
Worksheets("List1").Range("T7").Value = Worksheets("Jan").Range("C23").Value
Worksheets("List1").Range("D6").Value = Worksheets("Jan").Range("C7").Value
Worksheets("List1").Range("D7").Value = Worksheets("Jan").Range("C19").Value
Worksheets("List1").Range("Z7").Value = Worksheets("Jan").Range("C3").Value
Worksheets("List1").Range("Y7").Value = Worksheets("Jan").Range("C16").Value
Worksheets("List1").Range("Z6").Value = Worksheets("Jan").Range("C4").Value
Worksheets("List1").Range("Y6").Value = Worksheets("Jan").Range("C17").Value
Worksheets("List1").Range("N7").Value = Worksheets("Jan").Range("C5").Value
Worksheets("List1").Range("M16").Value = Worksheets("Jan").Range("C2").Value
Worksheets("List1").Range("D16").Value = Worksheets("Jan").Range("C16").Value
Worksheets("List1").Range("Y9").Value = Worksheets("Jan").Range("C15").Value
Worksheets("List1").Range("N11").Value = Worksheets("Jan").Range("C9").Value
Worksheets("List1").Range("Z8").Value = Worksheets("Jan").Range("C8").Value
Worksheets("List1").Range("Y8").Value = Worksheets("Jan").Range("C21").Value
ElseIf Day = 2 Then
Worksheets("List1").Range("I16").Value = Worksheets("Jan").Range("D10").Value
Worksheets("List1").Range("N6").Value = Worksheets("Jan").Range("D18").Value
'....etc
End If
End Function
Import Data by Month and Day
Note that you're writing C16 to D16 and to Y7.
Standard Module e.g. Module1
Option Explicit
Sub ImportData()
Const sfCol As Variant = "C" ' or 3
Dim sRows As Variant: sRows = VBA.Array( _
10, 18, 12, 11, 23, 7, 19, 3, 16, 4, _
17, 5, 2, 16, 15, 9, 8, 21)
Const dName As String = "List1"
Const dMonthAddress As String = "V5"
Const dDayAddress As String = "V6"
Dim dAddresses As Variant: dAddresses = VBA.Array( _
"I16", "N6", "T5", "T6", "T7", "D6", "D7", "Z7", "Y7", "Z6", _
"Y6", "N7", "M16", "D16", "Y9", "N11", "Z8", "Y8")
Dim dMonths As Variant: dMonths = VBA.Array( _
"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", _
"Nov", "Dec")
Dim wb As Workbook: Set wb = ThisWorkbook
Dim dws As Worksheet
On Error Resume Next
Set dws = wb.Worksheets(dName)
On Error GoTo 0
If dws Is Nothing Then
MsgBox "Destination worksheet not found."
Exit Sub
End If
Dim dMonth As Variant: dMonth = dws.Range(dMonthAddress).Value
If IsNumeric(dMonth) Then
dMonth = CLng(dMonth)
Else
MsgBox "Month is invalid"
Exit Sub
End If
If dMonth < 1 Or dMonth > 12 Then
MsgBox "Month is out of bounds."
Exit Sub
End If
Dim dDay As Variant: dDay = dws.Range(dDayAddress).Value
If IsNumeric(dDay) Then
dDay = CLng(dDay)
Else
MsgBox "Day is invalid."
Exit Sub
End If
If dDay < 1 Or dDay > 31 Then
MsgBox "Day is out of bounds."
Exit Sub
End If
Dim sws As Worksheet
On Error Resume Next
Set sws = dws.Parent.Worksheets(Application.Index(dMonths, dMonth))
On Error GoTo 0
If sws Is Nothing Then
MsgBox "Month worksheet does not exist."
Exit Sub
End If
Dim sCol As Long: sCol = sws.Columns(sfCol).Offset(, dDay - 1).Column
Dim sUpper As Long: sUpper = UBound(sRows)
Dim n As Long
For n = 0 To sUpper
dws.Range(dAddresses(n)).Value = sws.Cells(sRows(n), sCol).Value
Next n
End Sub
You can automate the previous with the following.
Sheet Module e.g. Sheet1 (List1)
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Range)
Const dMonthAddress As String = "V5"
Const dDayAddress As String = "V6"
Dim rg As Range: Set rg = Union(Range(dMonthAddress), Range(dDayAddress))
If Not Intersect(Target, rg) Is Nothing Then
ImportData
End If
End Sub
Now when you change the values in V5 or V6, the destination worksheet is automatically updated.
This is my take on this question:
Sub TEST()
Dim intDay, intMonth As Integer
Dim d As Integer
intDay = CInt(Range("V6").Value)
intMonth = CInt(Range("V5").Value)
' get the abbreviated month name
txtMonth = MonthName(intMonth, True)
' it seems that this is the output template
Set shtList = ThisWorkbook.Worksheets("List1")
' make sure that sheet of month name exists
Set shtMonth = ThisWorkbook.Worksheets(txtMonth)
d = 2 + intDay ' column index; e.g. d + Day = 2 + 1 = "C", 2 + 2 = "D"
With shtList
.Range("I16").Value = shtMonth.Cells(10, d).Value
.Range("N6").Value = shtMonth.Cells(18, d).Value
.Range("T5").Value = shtMonth.Cells(12, d).Value
.Range("T6").Value = shtMonth.Cells(11, d).Value
.Range("T7").Value = shtMonth.Cells(23, d).Value
.Range("D6").Value = shtMonth.Cells(7, d).Value
.Range("D7").Value = shtMonth.Cells(19, d).Value
.Range("Z7").Value = shtMonth.Cells(3, d).Value
.Range("Y7").Value = shtMonth.Cells(16, d).Value
.Range("Z6").Value = shtMonth.Cells(4, d).Value
.Range("Y6").Value = shtMonth.Cells(17, d).Value
.Range("N7").Value = shtMonth.Cells(5, d).Value
.Range("M16").Value = shtMonth.Cells(2, d).Value
.Range("D16").Value = shtMonth.Cells(16, d).Value
.Range("Y9").Value = shtMonth.Cells(15, d).Value
.Range("N11").Value = shtMonth.Cells(9, d).Value
.Range("Z8").Value = shtMonth.Cells(8, d).Value
.Range("Y8").Value = shtMonth.Cells(21, d).Value
End With
End Sub
Run this every change in day or change in month. Sheets based on month should exist. No error handling was considered. No need to repeat codes.
To illustrate my comment:
Sub TEST()
Dim Day As Long, Month As Long
Day = Range("V6").Value
Month = Range("V5").Value ' Not sure what you are doing with this one?
Worksheets("List1").Range("I16").Value = Worksheets("Jan").Cells(10, Day + 2).Value
Worksheets("List1").Range("N6").Value = Worksheets("Jan").Cells(18, Day + 2).Value
Worksheets("List1").Range("T5").Value = Worksheets("Jan").Cells(12, Day + 2).Value
Worksheets("List1").Range("T6").Value = Worksheets("Jan").Cells(11, Day + 2).Value
Worksheets("List1").Range("T7").Value = Worksheets("Jan").Cells(23, Day + 2).Value
Worksheets("List1").Range("D6").Value = Worksheets("Jan").Cells(7, Day + 2).Value
Worksheets("List1").Range("D7").Value = Worksheets("Jan").Cells(19, Day + 2).Value
Worksheets("List1").Range("Z7").Value = Worksheets("Jan").Cells(3, Day + 2).Value
Worksheets("List1").Range("Y7").Value = Worksheets("Jan").Cells(16, Day + 2).Value
Worksheets("List1").Range("Z6").Value = Worksheets("Jan").Cells(4, Day + 2).Value
Worksheets("List1").Range("Y6").Value = Worksheets("Jan").Cells(17, Day + 2).Value
Worksheets("List1").Range("N7").Value = Worksheets("Jan").Cells(5, Day + 2).Value
Worksheets("List1").Range("M16").Value = Worksheets("Jan").Cells(2, Day + 2).Value
Worksheets("List1").Range("D16").Value = Worksheets("Jan").Cells(16, Day + 2).Value
Worksheets("List1").Range("Y9").Value = Worksheets("Jan").Cells(15, Day + 2).Value
Worksheets("List1").Range("N11").Value = Worksheets("Jan").Cells(9, Day + 2).Value
Worksheets("List1").Range("Z8").Value = Worksheets("Jan").Cells(8, Day + 2).Value
Worksheets("List1").Range("Y8").Value = Worksheets("Jan").Cells(21, Day + 2).Value
End Sub
But yes, you can also put the data in arrays and such as well.
Hard to see much of a pattern here to short it down without understanding the data better.
The same idea can be applied to the month value if it's just to refer to another sheet, and if these sheets are in order. Like if "Jan" is sheet 2, and "Feb" is sheet 3 and so on:
Worksheets("List1").Range("I16").Value = Worksheets(Month + 1).Cells(10, Day + 2).Value

How to show results on desired sheet?

The below code is not showing the results on the "All Stock Analysis" sheet.
I tried doing a test after the activation of each worksheet (Range("I1).Interior.Color = vbGreen) and cell I1 turns green on each of the desired worksheets. What other tests can I try? No error msg pops up.
Sub AllStocksAnalysisRefactored()
Dim startTime As Single
Dim endTime As Single
yearValue = InputBox("What year would you like to run the analysis on?")
startTime = Timer
'Format the output sheet on All Stocks Analysis worksheet
Worksheets("All Stock Analysis").Activate
Range("A1").Value = "All Stocks (" + yearValue + ")"
'Create a header row
Cells(3, 1).Value = "Ticker"
Cells(3, 2).Value = "Total Daily Volume"
Cells(3, 3).Value = "Return"
'Initialize array of all tickers
Dim tickers(12) As String
tickers(0) = "AY"
tickers(1) = "CSIQ"
tickers(2) = "DQ"
tickers(3) = "ENPH"
tickers(4) = "FSLR"
tickers(5) = "HASI"
tickers(6) = "JKS"
tickers(7) = "RUN"
tickers(8) = "SEDG"
tickers(9) = "SPWR"
tickers(10) = "TERP"
tickers(11) = "VSLR"
'Activate data worksheet
Worksheets(yearValue).Activate
'Get the number of rows to loop over
RowCount = Cells(Rows.Count, "A").End(xlUp).Row
'1a) Create a ticker Index
Dim tickerIndex As Single
tickerIndex = 0
'1b) Create three output arrays
Dim tickerVolumes(12) As LongLong
Dim tickerstartingPrices(12) As Single
Dim tickerendingPrices(12) As Single
''2a) Create a for loop to initialize the tickerVolumes to zero.
For i = 0 To 11
tickerVolumes(i) = 0
''2b) Loop over all the rows in the spreadsheet.
For j = 2 To RowCount
'3a) Increase volume for current ticker
tickerVolumes(tickerIndex) = tickerVolumes(tickerIndex) + Cells(j, 8).Value
'3b) Check if the current row is the first row with the selected tickerIndex.
'If Then
If Cells(j - 1, 1).Value <> tickers(tickerIndex) And _
Cells(j, 1).Value = tickers(tickerIndex) Then
tickerstartingPrices(tickerIndex) = Cells(j, 6).Value
'End If
End If
'3c) check if the current row is the last row with the selected ticker
'If the next row’s ticker doesn’t match, increase the tickerIndex.
'If Then
If Cells(j + 1, 1).Value <> tickers(tickerIndex) And _
Cells(j, 1).Value = tickers(tickerIndex) Then
tickerendingPrices(tickerIndex) = Cells(j, 6).Value
'3d Increase the tickerIndex.
tickerIndex = tickerIndex + 1
'End If
End If
Next j
Next i
'4) Loop through your arrays to output the Ticker, Total Daily Volume, and Return.
For i = 0 To 11
Worksheets("All Stock Analysis").Activate
Next i
'Formatting
Worksheets("All Stock Analysis").Activate
Range("A3:C3").Font.FontStyle = "Bold"
Range("A3:C3").Borders(xlEdgeBottom).LineStyle = xlContinuous
Range("B4:B15").NumberFormat = "#,##0"
Range("C4:C15").NumberFormat = "0.0%"
Columns("B").AutoFit
dataRowStart = 4
dataRowEnd = 15
For i = dataRowStart To dataRowEnd
If Cells(i, 3) > 0 Then
Cells(i, 3).Interior.Color = vbGreen
Else
Cells(i, 3).Interior.Color = vbRed
End If
Next i
endTime = Timer
MsgBox "This code ran in " & (endTime - startTime) & _
" seconds for the year " & (yearValue)
End Sub
Here is how "All Stock Analysis" sheet will look after running the code:
You only need to scan the data sheet once if you use a dictionary object to convert the ticker ID to an array index number.
Option Explicit
Sub AllStocksAnalysisRefactored()
Const SHT_NAME = "All Stock Analysis"
Dim wb As Workbook, ws As Worksheet, wsYr As Worksheet
Dim cell As Range, yr As String, iRow As Long, iLastRow As Long
Dim t As Single: t = Timer
' choose data worksheet
yr = InputBox("What year would you like to run the analysis on ? ", "Enter Year", Year(Date))
Set wb = ThisWorkbook
On Error Resume Next
Set wsYr = wb.Sheets(yr)
On Error GoTo 0
' check if exists
If wsYr Is Nothing Then
MsgBox "Sheet '" & yr & "' does not exists.", vbCritical, "Error"
Exit Sub
End If
'Initialize array of all tickers
Dim tickerID, tickerData(), i As Integer, n As Integer
Dim dict As Object, sId As String
tickerID = Array("AY", "CSIQ", "DQ", "ENPH", "FSLR", "HASI", _
"JKS", "RUN", "SEDG", "SPWR", "TERP", "VSLR")
n = UBound(tickerID) + 1
ReDim tickerData(1 To n, 1 To 5)
' create dict id to index
Set dict = CreateObject("Scripting.Dictionary")
For i = 1 To n
sId = UCase(Trim(tickerID(i - 1)))
tickerData(i, 1) = sId ' id
tickerData(i, 2) = 0 ' volume
tickerData(i, 3) = 0 ' start price
tickerData(i, 4) = 0 ' finish price
tickerData(i, 5) = 0 ' count
dict.Add sId, i
Next
'Get the number of rows to loop over
iLastRow = wsYr.Cells(Rows.Count, "A").End(xlUp).Row
' Loop over all the rows in the spreadsheet.
' A=ticker, F=Price , H=Volume
For iRow = 2 To iLastRow
sId = UCase(Trim(wsYr.Cells(iRow, "A")))
If dict.exists(sId) Then
i = dict(sId)
' volume
tickerData(i, 2) = tickerData(i, 2) + wsYr.Cells(iRow, "H") ' volume
' start price when count is 0
If tickerData(i, 5) = 0 Then
tickerData(i, 3) = wsYr.Cells(iRow, "F")
End If
' end price
tickerData(i, 4) = wsYr.Cells(iRow, "F")
' count
tickerData(i, 5) = tickerData(i, 5) + 1
End If
Next
'Format the output sheet on All Stocks Analysis worksheet
Set ws = wb.Sheets(SHT_NAME)
ws.Cells.Clear
With ws
.Range("A1").Value2 = "All Stocks (" & yr & ")"
With .Range("A3:E3")
.Value2 = Array("Ticker", "Total Daily Volume", "Start Price", "End Price", "Return")
.Font.FontStyle = "Bold"
.Borders(xlEdgeBottom).LineStyle = xlContinuous
End With
.Range("A4").Resize(n, 4).Value2 = tickerData
.Range("B4:D4").Resize(n).NumberFormat = "#,##0"
.Range("E4").Resize(n).NumberFormat = "0.0%"
.Columns("B").AutoFit
End With
' coloring
For Each cell In ws.Range("E4").Resize(n)
cell.FormulaR1C1 = "=(RC[-1]-RC[-2])/RC[-2]" ' end-start/start
If cell > 0 Then
cell.Interior.Color = vbGreen
Else
cell.Interior.Color = vbRed
End If
Next
ws.Activate
ws.Range("A1").Select
MsgBox "This code ran for (" & yr & ")", vbInformation, Int(Timer - t) & " seconds"
End Sub

Type mismatch error in loop that adds numbers incrementally in column

I'm creating a sub that creates a time sheet for a specific month/year. The code is based on this Microsoft example code. The Microsoft code creates this calendar. I'm amending the code to insert the days of the week in a single column, like this.
My amended code correctly inserts the number 1 in the cell corresponding to the first day of the month, but the loop to add the subsequent day numbers does not work; Cell.Value = Cell.Offset(-1, 0).Value + 1 gives a Type Mismatch Error. Here is my amended code:
Sub Calendar_Genorator1()
Dim WS As Worksheet
Dim MyInput As Variant
Dim StartDay As Variant
Dim DayofWeek As Variant
Dim CurYear As Variant
Dim CurMonth As Variant
Dim FinalDay As Variant
Dim Cell As Range
Dim RowCell As Long
Dim ColCell As Long
Set WS = ActiveWorkbook.ActiveSheet
MyInput = InputBox("Type in Month and year for Calendar ")
If MyInput = "" Then Exit Sub
' Get the date value of the beginning of inputted month.
StartDay = DateValue(MyInput)
' Check if valid date but not the first of the month
' -- if so, reset StartDay to first day of month.
If Day(StartDay) <> 1 Then
StartDay = DateValue(Month(StartDay) & "/1/" & Year(StartDay))
End If
' Prepare cell for Month and Year as fully spelled out.
'Range("B3").NumberFormat = "d-mmmm"
'Set headers
Range("a1").Value = Application.Text(MyInput, "mmmm") & " Time Sheet"
Range("a2") = "Day"
Range("b2") = "Date"
Range("c2") = "Time In"
Range("d2") = "Time Out"
Range("e2") = "Hours"
Range("f2") = "Notes"
Range("g2") = "Overtime"
'Set weekdays
Range("a3") = "Sunday"
Range("a4") = "Monday"
Range("a5") = "Tuesday"
Range("a6") = "Wednesday"
Range("a7") = "Thursday"
Range("a8") = "Friday"
Range("a9") = "Saturday"
DayofWeek = Weekday(StartDay)
' Set variables to identify the year and month as separate variables.
CurYear = Year(StartDay)
CurMonth = Month(StartDay)
' Set variable and calculate the first day of the next month.
FinalDay = DateSerial(CurYear, CurMonth + 1, 1)
' Place a "1" in cell position of the first day of the chosen month based on DayofWeek.
Select Case DayofWeek
Case 1
Range("b3").Value = 1
Case 2
Range("b4").Value = 1
Case 3
Range("b5").Value = 1
Case 4
Range("b6").Value = 1
Case 5
Range("b7").Value = 1
Case 6
Range("b8").Value = 1
Case 7
Range("b9").Value = 1
End Select
'Loop through range b3:b44 incrementing each cell after the "1" cell.
For Each Cell In Range("b3:b44")
RowCell = Cell.Row
ColCell = Cell.Column
' Do if "1" is in column B or 2.
If Cell.Row = 1 And Cell.Column = 2 Then
' Do if current cell is not in 1st column.
ElseIf Cell.Row <> 1 Then
If Cell.Offset(-1, 0).Value >= 1 Then
Cell.Value = Cell.Offset(-1, 0).Value + 1 'Type Mismatch Error here
' Stop when the last day of the month has been entered.
If Cell.Value > (FinalDay - StartDay) Then
Cell.Value = ""
' Exit loop when calendar has correct number of days shown.
Exit For
End If
End If
End If
Next
End Sub
I changed the parameters in the loop to work inserting the days incrementally in column B, and I suspect the error is related to that. Any ideas as to why I'm getting an error for Cell.Value = Cell.Offset(-1, 0).Value + 1?
Monthly Calendar
Option Explicit
Sub Calendar_Genorator1()
Const TitleAddress As String = "A1"
Const HeadersAddress As String = "A2"
Const DaysAddress As String = "A3"
Dim Headers As Variant
Headers = Array("Day", "Date", "Time In", "Time Out", "Hours", _
"Notes", "Overtime")
Dim MyInput As Variant, StartDay As Variant
MyInput = InputBox("Type in setMonth and year for Calendar ")
If MyInput = "" Then Exit Sub
' Get the date value of the beginning of inputted Month.
StartDay = DateValue(MyInput)
' Check if valid date but not the first of the Month
' -- if so, reset StartDay to first day of Month.
If Day(StartDay) <> 1 Then
StartDay = DateValue(Month(StartDay) & "/1/" & Year(StartDay))
End If
Dim ws As Worksheet
Set ws = ActiveWorkbook.ActiveSheet
' Write title.
ws.Range(TitleAddress).Value = Application.Text(StartDay, "mmmm") _
& " Time Sheet"
' Write headers.
ws.Range(HeadersAddress).Resize(, UBound(Headers)) = Headers
' Write days.
Dim Target As Variant
Target = getDDDD_D_US(Month(StartDay), Year(StartDay))
ws.Range(DaysAddress).Resize(UBound(Target), UBound(Target, 2)).Value = Target
End Sub
Function getDDDD_D_US(setMonth As Long, setYear As Long)
Dim DaysData As Variant
DaysData = Array("Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", _
"Friday", "Saturday")
Dim Result As Variant
ReDim Result(1 To 42, 1 To 2)
' Write DDDD column.
Dim i As Long, j As Long, k As Long
For i = 1 To 6
k = (i - 1) * 7 + 1
For j = 0 To 6
Result(k + j, 1) = DaysData(j)
Next j
Next i
' Write D column.
Dim Current As Date
Current = DateSerial(setYear, setMonth, 1)
i = Weekday(Current)
For i = i To i + 27
Result(i, 2) = Day(Current)
Current = Current + 1
Next i
For i = i To i + 2
If Month(Current) = setMonth Then
Result(i, 2) = Day(Current)
Current = Current + 1
End If
Next i
getDDDD_D_US = Result
End Function

Loop through columns that vary from 1 to 31

I'm trying to do a calculation that will loop through multiple columns, the maximum would be 31 columns (31 days in a month).
Starting from "B8", next column would be "C8" ...
Excel template
I managed to calculate the first range.
I want to add a loop to calculate the number of hours for every existing column: Which could vary from 1 day to 31 days depending on the person using the excel template.
The number of projects could vary too "Code OTP" from 1 to 10 projects.
Update of my code:
Sub CalculHeuresTravail()
Application.ScreenUpdating = False
Dim i As Integer, firstDate As Date, secondDate As Date, n, rng As Range
Set ws = ThisWorkbook.Sheets("Feuil1")
MaSomme = 0
i = 8
f = 8
firstDate = ws.Range("E2")
secondDate = ws.Range("E3")
n = DateDiff("d", firstDate, secondDate)
n = n + 1
While ws.Range("A" & i).Value <> ""
i = i + 1
Wend
While f < i
MaSomme = MaSomme + ws.Range("B" & f).Value
f = f + 1
Wend
If MaSomme = "8,8" Then
MsgBox "OK"
Else: MsgBox "NON"
End If
End Sub
Here above is the solution that worked for me :
It loops through every column specified in a variable range stocked in a variable called 'e' that calculates the first column to start with and number of days specified by the user.
Sub CalculHeuresTravail()
Application.ScreenUpdating = False
Dim i As Integer, firstDate As Date, secondDate As Date, nombreDeJours, rng As Range, e
Set ws = ThisWorkbook.Sheets("Feuil1")
nombreDeProjets = 8
firstDate = ws.Range("E2")
secondDate = ws.Range("E3")
nombreDeJours = DateDiff("d", firstDate, secondDate)
nombreDeJours = nombreDeJours + 1
While ws.Range("A" & nombreDeProjets).Value <> ""
nombreDeProjets = nombreDeProjets + 1
Wend
IndiceDeColomne = 2
e = IndiceDeColomne + nombreDeJours
While IndiceDeColomne < e
caseDebutHeures = 8
MaSomme = 0
While caseDebutHeures < nombreDeProjets
MaSomme = MaSomme + ws.Cells(caseDebutHeures, IndiceDeColomne).Value
caseDebutHeures = caseDebutHeures + 1
ws.Cells(6, IndiceDeColomne).Value = MaSomme
Wend
If MaSomme = "8,8" Then
MsgBox "Colomne " & IndiceDeColomne & " OK"
Else: MsgBox "Colomne " & IndiceDeColomne & " NON OK"
End If
IndiceDeColomne = IndiceDeColomne + 1
Wend
End Sub

Resources