VBA Excel Function Error Runtimeerror 13 - vba

I cannot find the mistake. The error msg gives me "runtime error '13'" data types don't match. The error seems to be in the function "fillcalweeks".
Fillcalweeks should return an array which is filled with start and end dates which are located in a sheet.
Here's my code:
'For every calenderweek we need the start and end dates in an array to produce the timeline
Sub get_cal_weeks()
Dim weeks As Integer, i As Integer, col As String, weekstart As Date, weekend As Date
'start column is D
col = "D"
'get amount of weeks
weeks = countcalweeks()
'populate array calweeks
calweeks = fillcalweeks(weeks)
For i = 0 To weeks
Sheets("Kalenderwochen").Range("E" & i + 1) = calweeks(i, 1)
Next
End Sub
Function fillcalweeks(weeks As Integer) As String()
Dim i As Integer, datestart As Date, dateend As Date, calweek As Integer, returnarray() As String
For i = 0 To weeks
'date start & date end
datestart = Sheets("Kalenderwochen").Range("A" & i + 1).Value
dateend = Sheets("Kalenderwochen").Range("B" & i + 1).Value
calweek = Sheets("Kalenderwochen").Range("C" & i + 1).Value
returnarray(i, 1) = datestart
returnarray(i, 2) = dateend
returnarray(i, 3) = calweek
fillcalweeks = returnarray
Next
End Function
'Counts the calenderweeks in the Kalenderwochen sheet
Function countcalweeks() As Integer
countcalweeks = Sheets("Kalenderwochen").Range("A2").End(xlDown).row
End Function
Thx for the help

You are getting an error on the line calweeks = fillcalweeks(weeks) because you are assigning a String array (the result of the function fillcalweeks to a Variant.
You'll notice you declared every variable except calweeks. Since VBA doesn't have an explicit declaration for this variable it assigns it to a Variant.
To fix the problem, start by putting Option Explicit at the top of every module. When you compile the project, it'll alert you to errors such as these (Debug -> Compile VBA Project). Then all you need to do is declare calweeks as a String().
There is a second problem you have and that is that you are trying to store a Date data type in your String() array within the fillcalweeks. You either need to convert the datestart, dateend and calweek variables to Strings (you can use the VBA.CStr() function to do this) or change the function fillcalweeks to return a Date array.
Finally, you need to declare a size range for the returnarray() within fillcalweeks. VBA needs to know how big this is before it can fill the values. Since you know how many rows there are (its an input to the function) this is as simple as replacing the declaration of returnarray with ReDim returnarray(0 To weeks - 1, 1 to 3) As String. Note you want to dimension the array to weeks - 1 since you have a base of zero not one.
One more error is that when you output back to the worksheet the loop needs to be For i = 0 To weeks - 1 otherwise the array will be out of bounds...

Thx. I found all the mistakes in the code. 1. declaration of calweeks and 2. the array dimension:
'For every calenderweek we need the start and end dates in an array to produce the timeline
Sub get_cal_weeks()
Dim weeks As Integer, i As Integer, col As String, weekstart As Date, weekend As Date, calweeks() As Variant
'start column is D
col = "D"
'get amount of weeks
weeks = countcalweeks()
'populate array calweeks
calweeks = fillcalweeks(weeks)
For i = 0 To weeks
field = i + i + 4
weekstart = calweeks(i, 0)
weekend = calweeks(i, 1)
Cells(5, field) = monetary_calc_week(weekstart, weekend)
Next
End Sub
Function fillcalweeks(weeks As Integer) As Variant()
Dim i As Integer, datestart As Date, dateend As Date, calweek As Integer, arraysize As Integer, returnarray() As Variant
arraysize = 52
weeks = weeks - 2
ReDim Preserve returnarray(arraysize, 3)
For i = 0 To weeks
If i > arraysize Then
arraysize = arraysize * 2
ReDim Preserve returnarray(arraysize, 3)
End If
'date start & date end
datestart = Sheets("Kalenderwochen").Range("A" & i + 2).Value
dateend = Sheets("Kalenderwochen").Range("B" & i + 2).Value
calweek = Sheets("Kalenderwochen").Range("C" & i + 2).Value
returnarray(i, 0) = datestart
returnarray(i, 1) = dateend
returnarray(i, 2) = calweek
Next
fillcalweeks = returnarray
End Function
'Counts the calenderweeks in the Kalenderwochen sheet
Function countcalweeks() As Integer
countcalweeks = Sheets("Kalenderwochen").Range("A2").End(xlDown).row
End Function

Related

How to create an array of dates spaced 2 weeks apart in VBA?

I have seen examples on how to create an array of all the dates, or a date every month between a start date and end date here. However, I am trying to create an array of dates every 2 weeks specifically between a start and end date.
Ex. if startdate = 7/18/2021 and enddate = 12/28/2025, I want an array that is:
array = {7/18/2021, 8/1/2021, 8/15/2021, ..., 12/28/2025}
How exactly can I do this? I tried using a recorded macro but it only uses the xlFillDefault and doesn't actually specify the algorithm used.
Sub get2weekdates()
'
' get2weekdates Macro
'
'
ActiveCell.FormulaR1C1 = "7/18/2021"
Range("D6").Select
ActiveCell.FormulaR1C1 = "8/1/2021"
Range("D5:D6").Select
Selection.AutoFill Destination:=Range("D5:D121"), Type:=xlFillDefault
Range("D5:D121").Select
End Sub
The idea of the Function ArrayWithDates() is to take 2 parameters - the first day and the size as an optional one (in the example, 14 is 2 weeks, you may write 21 for 3 weeks or 20 for 2 weeks and 6 days).
Once it gets these, it enlarges the array based on the size with this line - ReDim result(size - 1). As the 0th element of the array is known, it is assigned outside the loop - result(0) = firstDay.
Then, in the loop, the other elements are assigned, and each one is the next day, using DateAdd():
Public Sub TestMe()
Dim i As Long
Dim myArray As Variant
myArray = ArrayWithDates(#7/18/2021#)
For i = LBound(myArray) To UBound(myArray)
Debug.Print i; myArray(i)
Next i
End Sub
Public Function ArrayWithDates(firstDay As Date, Optional size As Long = 14) As Variant
Dim i As Long
If size < 1 Then
ArrayWithDates = Array()
Exit Function
End If
ReDim result(size)
result(0) = firstDay
For i = 1 + LBound(result) To UBound(result)
result(i) = DateAdd("D", 1, result(i - 1))
Next i
ArrayWithDates = result
End Function
The result looks as expected:
Dates are just doubles that look like a date. we just need to loop and add 14 to the start till we hit the end date:
Sub get2weekdates()
With ActiveSheet
Dim strtdt As Double
strtdt = .Range("C1").Value2
Dim eddt As Double
eddt = .Range("c2").Value2
Dim nmDts As Long
nmDts = (eddt - strtdt) / 14 + 1
Dim otArray As Variant
ReDim otArray(1 To nmDts, 1 To 1)
Dim i As Long
For i = 1 To nmDts
otArray(i, 1) = strtdt + ((i - 1) * 14)
Next i
.Range("D5").Resize(nmDts, 1).Value = otArray
.Range("D5").Resize(nmDts, 1).NumberFormat = "mm/dd/yyyy"
End With
End Sub
But as stated by #pdtcaskey with Office 365 this can be a simple formula:
=SEQUENCE((C2 - C1)/14 + 1,, C1, 14)

Ignore, or avoid circular reference in UDF?

I am trying to make a planner. I have a table which I want to be something like this:
ID
Parent ID
Start Date
Duration
End Date
1
01/01/2021
10
11/01/2021
2
1
06/05/2021
2
08/05/2021
3
2
08/05/2021
1
09/05/2021
4
3
09/05/2021
5
14/05/2021
5
2,3,4
14/05/2021
4
18/05/2021
The Start date will be based on matching the Parent ID to the Task ID, so task 2 follows task 1, task 2 follows task 3 etc. I also want to be able to test against multiple tasks that might be happening at the same time, so task 5 can start after tasks 2, 3 or 4, which ever ends last.
I wrote this UDF for the start date calculation.
Option Explicit
Function LastPredecessor(PreList As String, TaskList As Range, TaskDueDate As Range)
Dim ID
Dim Dates
Dim nPres, i As Integer
On Error Resume Next
Pres = Split(PreList, ",")
nPres = UBound(Pres)
ReDim Dates(0 To nPres, 0)
For i = 0 To nPres
Dates(i, 0) = IDX_Match(CInt(Pres(i)), TaskList, TaskDueDate)
Next
LastPredecessor = WorksheetFunction.Max(Dates)
End Function
Function IDX_Match(LookupVal, MatchRange As Range, LookupRange As Range, Optional MatchType As Integer = 0)
IDX_Match = WorksheetFunction.Index(LookupRange.Value2, WorksheetFunction.Match(LookupVal, MatchRange.Value2, MatchType))
End Function
The function is called in the start date in the table like this
=LastPredecessor([#Parent ID],[ID],[End Date])
It works well if the end date is independent of the start date, but as soon as I try to update the end date, and add start date + duration as the calculation for end date, it generates a circular error.
I am sure I am doing something very simple wrong, but would be great to know what I need to do to fix this.
EDIT:
Thanks to #Toddleson, the following is a slightly modified version
Function LastPredecessor(PreList As String, EndDates As Range, IDColumn As Range) As Date
Dim Preds() As String, PredDates() As Long
Preds = Split(PreList, ",")
ReDim PredDates(UBound(Preds))
For i = LBound(Preds) To UBound(Preds)
PredDates(i) = IDColumn(WorksheetFunction.Match(CInt(Preds(i)), IDColumn, 0)).Offset(0, EndDates.Column - IDColumn.Column).Value2
Next i
LastPredecessor = Application.WorksheetFunction.Max(PredDates)
End Function
Ok, I rewrote the function. It should be doing exactly what you described to me.
Function LastPredecessorV2(PreList As String, EndDates As Range, IDColumn As Range) As Date
Dim Preds() As String, PredDates() As Long
Preds = Split(Replace(PreList, " ", ""), ",")
ReDim PredDates(UBound(Preds))
For i = LBound(Preds) To UBound(Preds)
PredDates(i) = IDColumn.Find( _
What:=CInt(Preds(i)), _
LookAt:=xlWhole _
).Offset(0, EndDates.Column - IDColumn.Column).Value2 'Corrected
Next i
LastPredecessorV2 = CDate(Application.WorksheetFunction.Max(PredDates))
End Function
Here's how the input of the arguments looks. Those ranges can be the entire column and it still works.
All of the End Dates are using the formula (Start Date + Duration in Days). The first task start date is the only independent value in the column, all the other ones are using the formula.
Edit:
I should mention that you can insert columns in-between the ranges and the function will still work.

String Value is not passing correctly

I have a word table. I wrote a macro to get values from the table. When it runs I get a runtime error 13. When I debug and watch the value of parsing string it looks like this "2019-04-03 There is only one quote in the string. I think that is the case I couldn't convert that string into a date format. Can you help me to fix this?
The code
Sub Macro2()
Dim NumRows As Integer
Dim startDate As String
Dim days As String
Dim endDate As String
If Not Selection.Information(wdWithInTable) Then
Exit Sub
End If
NumRows = Selection.Tables(1).Rows.Count
'Loop to select each row in the current table
For J = 2 To NumRows
'Loop to select each cell in the current row
startDate = Selection.Tables(1).Rows(J).Cells(5).Range.Text
days = Selection.Tables(1).Rows(J).Cells(6).Range.Text
FormatDate = CDate(ends)
endDate = DateAdd("d", days, FormatDate)
Selection.Tables(1).Rows(J).Cells(7).Range.Text = endDate
Next J
End Sub
The table
Here's the minimal change I found that works for me when tested in Word 2013.
General points:
I added Option Explicit so that the computer would help me find errors. In this case, the variables J and FormatDate were used but not Dimed, and ends was used but never initialized (I changed it to startDate).
The Range.Text in a table cell includes whitespace and the end-of-table marker (ยค). That is why CDate was giving an error.
For the dates, I used Left() to take only the left ten characters, since you seem to always be using yyyy-mm-dd-format dates.
For the counts of days, since those can be of any length, I used Range.Words(1).Text to keep only the first Word (as MS Word defines it), which is the number.
I also added the CLng() call in the parameter to DateAdd, since DateAdd wants a number* rather than a string.
For production use, I would also recommend using Selection only in one place, and doing Dim workTable as Table: Set workTable = Selection.Tables(1). That will simplify your code.
Code
<=== marks changed lines
Option Explicit ' <==
Sub Macro2()
Dim NumRows As Integer
Dim startDate As String
Dim days As String
Dim endDate As String
If Not Selection.Information(wdWithInTable) Then
Exit Sub
End If
NumRows = Selection.Tables(1).Rows.Count
'Loop to select each row in the current table
Dim J As Long ' <==
For J = 2 To NumRows
'Loop to select each cell in the current row
startDate = Selection.Tables(1).Rows(J).Cells(5).Range.Text
startDate = Left(startDate, 10) ' <== Remove the space and table mark
days = Selection.Tables(1).Rows(J).Cells(6).Range.Words(1).Text ' <===
Dim FormatDate As Date ' <==
FormatDate = CDate(startDate) ' <== not `ends`
endDate = DateAdd("d", CLng(days), FormatDate) ' <=== clng
Selection.Tables(1).Rows(J).Cells(7).Range.Text = endDate
Next J
End Sub
* DateAdd actually takes a Double, but VBA can promote Long to Double. I chose CLng since it looks like you are only using integer day spans. If not, use CDbl instead.
Try:
Sub Demo()
Dim r As Long
With Selection
If Not .Information(wdWithInTable) Then Exit Sub
With .Tables(1)
For r = 2 To .Rows.Count
.Cell(r, 7).Range.Text = _
Format(DateAdd("d", Split(.Cell(r, 6).Range.Text, vbCr)(0), CDate(Split(.Cell(r, 5).Range.Text, vbCr)(0))), "YYYY-MM-DD")
Next r
End With
End With
End Sub

How to use XIRR from VBA Script

I have an Excel spreadsheet that shows deposits to a bank account, plus periodically I check the balance and put that in the spreadsheet. I'd like to make a macro that calculates the to-date equivalent return (i.e., if I have this sequence of deposits, and then the account balance is $X, then the equivalent rate of interest is Y%). For example, I might have:
1-Jan-2010 $100
22-Apr-2011 $200
14-Feb-2012 $310
28-Aug-2013 $500
9-May-2014 $790 # account lost value!
I can get the dates and deposits into a function (along with the ending balance) but I can't get XIRR to work right. I know this is ugly VBA (not a language I know) but here's what I have:
Public Function MyXIRR(Dates As Range, Trans As Range, Balance As Double)
Dim i As Integer
Dim x As Double
Dim dateArray() As Date
Dim valArray() As Double
ReDim dateArray(Dates.Count + 1)
ReDim valArray(Trans.Count + 1)
For i = 1 To Dates.Count
dateArray(i - 1) = Dates.Item(i)
Next i
For i = 1 To Trans.Count
valArray(i - 1) = Trans.Item(i)
Next i
dateArray(Dates.Count) = DateAdd("d", 1, Dates.Item(Dates.Count))
valArray(Trans.Count) = -1 * Balance
For i = 0 To Dates.Count
dateArray(i) = Format(dateArray(i), "dd/mm/yyyy")
Next i
MsgBox ("Hello")
x = Application.Xirr(valArray, dateArray, 0.01)
MsgBox (x)
' MyXIRR = Dates.Count * 100 + Trans.Count
' MyXIRR = dateArray(6)
MyXIRR = valArray(3)
End Function
(The comments and MsgBox at the end are my various attempts to figure out what's going on; ideally, I'd just have a formula in a cell: =MyXIRR( A2:A6, B2:B6, C6 ) which would return the equivalent interest rate.)
I think what's happening is that when I call Application.Xirr, I'm getting a #VALUE exception (error?) But I don't know how to debug it, examine the arrays passed to Xirr, or figure out what's wrong.
I don't know whether this is The Answer, or whether I've merely swept the problem under a different rug, but I was able to get my function to work reliably by converting the dateArray to an array of Strings, where each String is a parseable date.
Public Function MyXIRR(Dates As Range, Trans As Range, Balance As Double)
Dim i As Integer
Dim x As Double
Dim dateArray() As Date
Dim dateStrings() As String
Dim valArray() As Double
ReDim dateArray(Dates.Count)
ReDim valArray(Trans.Count)
ReDim dateStrings(Dates.Count)
For i = 1 To Dates.Count
dateArray(i - 1) = Dates.Item(i).Value
Next i
For i = 1 To Trans.Count
valArray(i - 1) = Trans.Item(i).Value
Next i
dateArray(Dates.Count) = DateAdd("d", 1, Dates.Item(Dates.Count))
valArray(Trans.Count) = -1 * Balance
For i = 0 To Dates.Count
dateStrings(i) = Format(dateArray(i), "mm/dd/yyyy")
Next i
MyXIRR = Application.WorksheetFunction.Xirr(valArray, dateStrings, 0.01)
End Function

Type mismatch when returning an array from a function

I am trying to create a function, for use in subs, that creates an array of actual days in a month. I am unable to run the function by using either the Immediate window directly or with a sub. The function's code is the following (Debug.Print instances are removed, as they never even trigger):
Function GenererDager(ByVal month As Integer, ByVal year As Integer) As Integer
Dim dateFormat As String, daysArr() As Integer, actualDays As Integer
actualDays = 1
' Find the number of actual days in given month
For dayCheck = 1 To 31
dateFormat = month & "/" & dayCheck & "/" & year ' mm/dd/yyyy format
If IsDate(dateFormat) Then
actualDays = actualDays + 1
Else
Exit For
End If
Next dayCheck
' Redimension the array with the actual number of days in the month
ReDim daysArr(actualDays)
' Populate the array with the correct number of days
For daysToArray = 1 To actualDays
daysArr(daysToArray) = daysToArray
Next daysToArray
GenererDager = daysArr
End Function
Running the function using the Immediate window with GenererDager(2, 2017) produces the following error message: Compile error: Expected: =
Running the function using the Immediate window with ?GenererDager(2, 2017) produces the following error message: Compiler error: Type mismatch with the very last usage of daysArr selected
The test-sub I'm using to call the function looks like this:
Sub HentDager()
Dim daysArray() As Integer
daysArray = GenererDager(2, 2017)
Debug.Print daysArray(4)
End Sub
Calling this sub in the Immediate window with HentDager() produces the following error: Compile error: Expected: =
I have been pretty much stuck on this problem for a while now, and I have been rewriting the code several times to identify the issue(s), but so far I have been unable to solve it. I might have produced more errors than what I have fixed in the process, as it have dawned on me that I have no clue what I'm really doing right now :-)
You have to declare your objects and function as Variant to pass arrays.
Further more, your function needed a few changes :
actualDays = actualDays - 1 because you returned the 1st value for which it isn't a date!
ReDim daysArr(1 To actualDays) to avoid having an empty value on the 1st index of the array,
which would have been daysArr(0)
Working function (tested):
Function GenererDager(ByVal month As Integer, ByVal year As Integer) As Variant
Dim dateFormat As String, daysArr() As Variant, actualDays As Integer, dayCheck As Integer, daysToArray As Integer
actualDays = 1
' Find the number of actual days in given month
For dayCheck = 1 To 31
dateFormat = month & "/" & dayCheck & "/" & year ' mm/dd/yyyy format
If IsDate(dateFormat) Then
actualDays = actualDays + 1
Else
Exit For
End If
Next dayCheck
actualDays = actualDays - 1
' Redimension the array with the actual number of days in the month
ReDim daysArr(1 To actualDays)
' Populate the array with the correct number of days
For daysToArray = 1 To actualDays
daysArr(daysToArray) = daysToArray
Next daysToArray
GenererDager = daysArr
End Function
Tests :
Sub HentDager()
Dim daysArray() As Variant
daysArray = GenererDager(2, 2017)
'Display last day of the month in the immediate window
Debug.Print daysArray(UBound(daysArray))
End Sub
After a bit of trial and error, here is an example using Integer :
Sub TestIntSub()
Dim TestInt() As Integer
TestInt = FctIntArr
Debug.Print TestInt(1) & "|" & TestInt(2)
End Sub
Public Function FctIntArr() As Integer()
Dim IntArr() As Integer
ReDim IntArr(1 To 2)
IntArr(1) = 545
IntArr(2) = 232
FctIntArr = IntArr
End Function
You need to define the return type of the function correctly -
Function GenererDager(ByVal month As Integer, ByVal year As Integer) As Integer
Change it to
Function GenererDager(ByVal month As Integer, ByVal year As Integer) As Variant
few things that seems to be wrong are
your return value of the function is actually dimmed as an integer rather than as integer()
instead of dimming the daysarray as array just dim it as a variant and assign the value