sick leave table using vba - excel

I couldn't figure out what the problem which cause "type mismatch" error
when i click the command button for the first time it shows the error , when i click again it works fine.
so can u help me to figure out what cause this error
red circle in the line which cause the error
Private Sub CommandButton1_Click()
Const cStart As Long = 5
Const cEnd As Long = 6
Const cDura As Long = 8
' Define Workbook.
Dim wb As Workbook: Set wb = ThisWorkbook
Dim srg As Range 'range refer to cell/multiple cells/row/column
Dim Sec As Range
Set srg = wb.Worksheets("info").Range("A2").CurrentRegion 'all data
Set Sec = wb.Worksheets("new").Range("A1").CurrentRegion
' Write values from Source Range to Data Array.
Dim Data As Variant: Data = srg.Value 'variant any type of data
Dim Dataa As Variant: Dataa = Sec.Value
Dim srCount As Long: srCount = UBound(Data, 1) ' Source Number of Rows , UBound return size ofarray
Dim cCount As Long: cCount = UBound(Data, 2) ' return Number of Columns
' Define Days Array.
Dim dData As Variant: ReDim dData(2 To srCount) 'array, ReDim increase number without deleting previous data
Dim countDura As Variant: ReDim countDura(srCount)
Dim rrCount As Long: rrCount = 1 ' Result Array Row Counter - 1 for headers
Dim cDiff As Long ' Days Between First and Last incl.
Dim dura As Long
Dim i As Long ' Data (Source) Array Rows
Dim d As Long
' Calculate Result Array Rows Count and populate Days Array.
For i = 2 To srCount
cDiff = DateDiff("M", Data(i, cStart), Data(i, cEnd)) + 1
dData(i) = cDiff
rrCount = rrCount + cDiff
Next i
For d = 3 To srCount
dura = Data(d, cEnd) - Data(d, cStart)
countDura(d) = dura
rrCount = rrCount + dura
Next d
' Define Result Array.
Dim Result As Variant: ReDim Result(1 To rrCount, 1 To cCount)
Dim k As Long: k = 1 ' Result Array Rows Counter - 1 for headers
' Declare additional variables.
Dim j As Long ' Data and Result Array Columns Counter
Dim n As Long ' Repeat Counter
' Write headers.
For j = 1 To cCount
Result(1, j) = Data(1, j)
Next j
' Write 'body'.
For i = 2 To srCount 'row
For n = 1 To dData(i) 'diff
k = k + 1
For j = 1 To cCount 'column
Select Case j
Case cStart 'column 5 (started date)
If dData(i) = 1 Then 'print the same dates if the diff is 1
Result(k, j) = Data(i, j)
Result(k, cEnd) = Data(i, cEnd)
Else
If n = 1 Then
Result(k, j) = Data(i, j) 'the first row of diff then change last date of month
Result(k, cEnd) = dateLastInMonth(Data(i, j))
Else
If n = dData(i) Then 'if n = diff , change first date depnd on last date(last row of diff)
Result(k, j) = dateFirstInMonth(Data(i, cEnd))
Result(k, cEnd) = Data(i, cEnd)
Result(k, cDura) = Data(i, cDura)
Else ' previous empty row
Result(k, j) = Result(k - 1, cEnd) + 1
Result(k, cEnd) = dateLastInMonth(Result(k, j))
Result(k, cDura) = Data(i, cDura)
End If
End If
End If
Case Is <> cEnd ' not equal
'
Result(k, j) = Data(i, j) '' print rest of data
Result(k, cDura) = Data(i, cDura)
End Select
Next j
Next n
Next i
With wb.Worksheets("new").Range("A1").Resize(, cCount)
.Resize(k).Value = Result
.Resize(.Worksheet.Rows.Count - .Row - k + 1).Offset(k).ClearContents
Dim shDest As Worksheet
Set shDest = ActiveWorkbook.Sheets("new")
Dim last_row As Variant
last_row = shDest.Cells(Rows.Count, 2).End(xlUp).Row
Dim daysDura As Variant
Dim countDur As Variant: ReDim countDur(last_row)
For i = 2 To last_row 'row
shDest.Cells(i, 8).ClearContents
daysDura = DateDiff("d", Dataa(i, cStart), Dataa(i, cEnd))
countDur(i) = daysDura
shDest.Cells(i, 8).Value = countDur(i)
Next i
End With
End Sub
Function dateFirstInMonth( _
ByVal d As Date) _
As Date
dateFirstInMonth = DateSerial(Year(d), Month(d), 1)
End Function
Function dateLastInMonth( _
ByVal d As Date) _
As Date
If Month(d) = 12 Then
dateLastInMonth = DateSerial(Year(d), 12, 31)
Else
dateLastInMonth = DateSerial(Year(d), Month(d) + 1, 1) - 1
End If
End Function
The code is to shows the leave dates and calculate the number of days between the dates

Related

Finding Largest repeated letter between two columns a/o rows

I try to find largest consecutive letter between two dynamic colums.
Below code find largest consequent letter below same column (like C10:C50) however I want to check different range like "D13:D23;E9:E12". Below code brings me fault. Also can anyone help me to how I can convert it to row defined.
Function CountConsVal(r As Range)
Dim i As Long, s As Long
Rng = r.Value
For i = LBound(Rng, 1) To UBound(Rng, 1) - 1
If Rng(i, 1) = Rng(i + 1, 1) Then
s = s + 1
Rng(i, 1) = ""
Else
Rng(i, 1) = s + 1
s = 0
End If
Next i
Rng(UBound(Rng), 1) = s + 1
CountConsVal = Rng
End Function
Counting Consecutive Group Members
This is the same function you provided a little more readable with some minor changes.
Range("B1:B6").Value = GetGroupCountCols(Range("A1:A6"))
A B
1 a
2 a
3 a 3
4 b
5 b 2
6 c 1
Function GetGroupCountCols(ByVal rg As Range) As Variant
If rg Is Nothing Then Exit Function
Dim drCount As Long: drCount = rg.Rows.Count
Dim cData As Variant
If drCount = 1 Then ' one cell
ReDim cData(1 To 1, 1 To 1): cData(1, 1) = 1
Else ' multiple cells
cData = rg.Columns(1).Value ' ensure one column
Dim r As Long, rCount As Long
For r = 1 To drCount - 1
If cData(r, 1) = cData(r + 1, 1) Then
cData(r, 1) = Empty: rCount = rCount + 1
Else
cData(r, 1) = rCount + 1: rCount = 0
End If
Next r
cData(drCount, 1) = rCount + 1
End If
GetGroupCountCols = cData
End Function
This is the same function but for rows.
Range("A2:F2").Value = GetGroupCountRows(Range("A1:F1"))
A B C D E F
1 a a a b b c
2 3 2 1
Function GetGroupCountRows(ByVal rg As Range) As Variant
If rg Is Nothing Then Exit Function
Dim dcCount As Long: dcCount = rg.Columns.Count
Dim rData As Variant
If dcCount = 1 Then ' one cell
ReDim rData(1 To 1, 1 To 1): rData(1, 1) = 1
Else ' multiple cells
rData = rg.Rows(1).Value ' ensure one row
Dim c As Long, cCount As Long
For c = 1 To dcCount - 1
If rData(1, c) = rData(1, c + 1) Then
rData(1, c) = Empty: cCount = cCount + 1
Else
rData(1, c) = cCount + 1: cCount = 0
End If
Next c
rData(1, dcCount) = cCount + 1
End If
GetGroupCountRows = rData
End Function
This is a test for the following two functions:
Sub GetGroupColumnsCountTEST()
Dim rg As Range: Set rg = Range("I10:I12,F4:F6,G7:G9")
Debug.Print rg.Address
Dim Data As Variant: Data = GetGroupColumnsCount(GetMultiColumns(rg))
Dim rg2 As Range: Set rg2 = Range("K4").Resize(UBound(Data, 1))
rg2.Value = Data
End Sub
This function will return the values of multiple column ranges in a 2D one-based one-column array.
Range("F1:F6").Value = GetMultiColumns(Range("E6,A1:A3,C4:C5")) ' see previous procedure
A B C D E F
1 a a
2 a a
3 b b
4 c c
5 c c
6 d d
Function GetMultiColumns(ByVal mrg As Range) As Variant
If mrg Is Nothing Then Exit Function
' Write data to a jagged array.
Dim aCount As Long: aCount = mrg.Areas.Count
Dim aData As Variant: ReDim aData(1 To aCount, 1 To 3)
Dim Help As Variant: ReDim Help(1 To 1, 1 To 1)
Dim a As Long
Dim arCount As Long, drCount As Long
For a = 1 To aCount
With mrg.Areas(a)
aData(a, 1) = .Row
aData(a, 2) = .Rows.Count
drCount = drCount + aData(a, 2)
If aData(a, 2) = 1 Then
aData(a, 3) = Help: aData(a, 3)(1, 1) = .Value
Else
aData(a, 3) = .Value
End If
End With
Next a
' Bubble sort the array by its first column (first rows) ascending.
ReDim Help(1 To 1)
Dim b As Long, c As Long
For a = 1 To aCount - 1
For b = a To aCount
If aData(a, 1) > aData(b, 1) Then
For c = 1 To 3
Help(1) = aData(a, c)
aData(a, c) = aData(b, c)
aData(b, c) = Help(1)
Next c
End If
Next b
Next a
' Write result.
Dim dData As Variant: ReDim dData(1 To drCount, 1 To 1)
Dim dr As Long
For a = 1 To aCount
Dim r As Long, rCount As Long
For r = 1 To aData(a, 2)
dr = dr + 1
dData(dr, 1) = aData(a, 3)(r, 1)
Next r
Next a
GetMultiColumns = dData
End Function
This is the same as your function but it takes a 2D one-based one-column array instead of a one-column range as the argument.
Function GetGroupColumnsCount(ByVal sData As Variant) As Variant
If IsEmpty(sData) Then Exit Function
Dim drCount As Long: drCount = UBound(sData, 1)
Dim cData As Variant: ReDim cData(1 To drCount, 1 To 1)
If drCount = 1 Then
cData(1, 1) = sData(1, 1)
Else
Dim r As Long, rCount As Long
For r = 1 To drCount - 1
If sData(r, 1) = sData(r + 1, 1) Then
rCount = rCount + 1
Else
cData(r, 1) = rCount + 1: rCount = 0
End If
Next r
cData(drCount, 1) = rCount + 1
End If
GetGroupColumnsCount = cData
End Function
To conclude, the last two functions do what you primarily requested. The only job for you is to combine them into one if necessary.
It seems you want to determine the maximum consecutive "r" values in each of many rows, one row at a time.
I suggest a User Defined Function with a one-row argument
Optionally check that the range argument is valid
read the range into a variant array for faster processing
Use a dictionary to collect each consecutive group of r's
Iterate through the dictionary to find the longest
Divide the final count by two to convert to hours
I used early-binding for the Dictionary object, but you can use late-binding if you prefer. Early-binding may execute slightly faster.
'Set reference to Microsoft Scripting Runtime
Option Explicit
Option Compare Text 'case insensitive
Function LongestConsecutiveRestingHrs(rw As Range) As Double
Dim vRw As Variant, v As Variant
Dim dict As Dictionary
Dim lCount As Long
Dim I As Long
'Optional sanity check: eg:
'confirm rw is 48 columns x 1 row
'If Not rw.Rows.Count = 1 Or Not rw.Columns.Count = 48 Then
' MsgBox "Invalid Range: " & rw.Address & vbLf & "Please enter valid range"
' Exit Function
'read into variant array for faster processing
vRw = rw
'count consecutive "r" groups
Set dict = New Dictionary
I = 0
For Each v In vRw 'can do this since vRw will be a 1D array
If v <> "r" Then
I = I + 1
Else
dict(I) = dict(I) + 1
End If
Next v
'find max r
I = 0
For Each v In dict.Keys
I = IIf(I > dict(v), I, dict(v))
Next v
LongestConsecutiveRestingHrs = I / 2
End Function

Why is my array breaking the column when I use TRANSPOSE to paste it into a worksheet?

In Excel, I'm using VBA to create an array to collect data and then pasting it back into a worksheet. This functioned excellently on a smaller dataset (~15,000 rows), but when I move to my larger dataset (~117,000 rows), something is happening at the "Transpose" step.
In the array, I have headers and data that I want to paste into 5 columns in a new sheet starting at cell B5. I define the range ("ListDestination"), then paste it in using this code:
shNew.Name = shName
Set ListDestination = shNew.Range("B5").Resize(UBound(arrList, 2), UBound(arrList, 1))
ListDestination = WorksheetFunction.Transpose(arrList)
When I check the ListDestination in the immediate window, it is correct ($B$5:$F$116771) and in the Watches window, I can see that the arrList is defined (1 to 5, 0 to 116767), which is correct. When expanding it, it also shows the data in the correct places. However, after the "Transpose" line, the result in the worksheet is:
...whereas it should be:
For what it's worth, it does paste through the entire "ListDestination" range, but after row 51236 all I get is #N/A:
I haven't changed anything in the code since this worked on the smaller dataset, so I'm thinking it must have something to do with the size of the dataset.
Thanks for any help you can provide.
Here is a simple function that will transpose the array.
Function my_transpose(arr As Variant) As Variant()
Dim tempArray() As Variant
ReDim tempArray(LBound(arr, 2) To UBound(arr, 2), LBound(arr, 1) To UBound(arr,1)) As Variant
Dim i As Long
For i = LBound(arr, 1) To UBound(arr, 1)
Dim j As Long
For j = LBound(arr, 2) To UBound(arr, 2)
tempArray(j, i) = arr(i, j)
Next j
Next i
my_transpose = tempArray
End Function
Then you would use in your line like this:
shNew.Name = shName
Set ListDestination = shNew.Range("B5").Resize(UBound(arrList, 2), UBound(arrList, 1))
ListDestination = my_transpose(arrList)
The Limited Transpose Function
The Solution
Using the transpose2D function you could do one of the following:
' Note the '+ 1' since the lower limit of the second dimension is '0'.
Set ListDestination _
= shNew.Range("B5").Resize(UBound(arrList, 2) + 1, UBound(arrList, 1))
ListDestination.Value = transpose2D(arrList)
' No need for '+ 1' since '1' is used with 'transpose2D'.
Dim Data As Variant: Data = transpose2D(arrList, 1)
Set ListDestination _
= shNew.Range("B5").Resize(UBound(Data, 1), UBound(Data, 2))
ListDestination.Value = Data
' No need for '+ 1' since '1' is used with 'transpose2D'.
arrList = transpose2D(arrList, 1)
Set ListDestination _
= shNew.Range("B5").Resize(UBound(arrList, 1), UBound(arrList, 2))
ListDestination.Value = arrList
The Function
Function transpose2D( _
ByVal TwoD As Variant, _
Optional ByVal FirstIndex As Variant) _
As Variant
Dim LB1 As Long: LB1 = LBound(TwoD, 1)
Dim UB1 As Long: UB1 = UBound(TwoD, 1)
Dim LB2 As Long: LB2 = LBound(TwoD, 2)
Dim UB2 As Long: UB2 = UBound(TwoD, 2)
Dim Data As Variant, r As Long, c As Long
If IsMissing(FirstIndex) Then ' just transpose
ReDim Data(LB2 To UB2, LB1 To UB1)
For r = LB2 To UB2
For c = LB1 To UB1
Data(r, c) = TwoD(c, r)
Next c
Next r
Else ' transpose with (possibly) modified limits: LB1 = LB2 = FirstIndex
Dim D1 As Long: D1 = FirstIndex - LB1
Dim D2 As Long: D2 = FirstIndex - LB2
ReDim Data(FirstIndex To UB2 + D2, FirstIndex To UB1 + D1)
For r = LB2 To UB2
For c = LB1 To UB1
Data(r + D2, c + D1) = TwoD(c, r)
Next c
Next r
End If
transpose2D = Data
End Function
A Simple Example
Sub transpose2DTEST()
Dim TwoD As Variant: ReDim TwoD(1 To 2, 0 To 3) ' Note the zero (0)
Dim r As Long, c As Long, n As Long
For r = 1 To UBound(TwoD, 1)
For c = 0 To UBound(TwoD, 2)
n = n + 1
TwoD(r, c) = n
Next c
Next r
' Contents of TwoD:
' TwoD(1, 0) = 1
' TwoD(1, 1) = 2
' TwoD(1, 2) = 3
' TwoD(1, 3) = 4
' TwoD(2, 0) = 5
' TwoD(2, 1) = 6
' TwoD(2, 2) = 7
' TwoD(2, 3) = 8
Dim Data As Variant
Data = transpose2D(TwoD) ' just tranpose (note the zero)
' Contents of Data:
' Data(0, 1) = 1
' Data(0, 2) = 5
' Data(1, 1) = 2
' Data(1, 2) = 6
' Data(2, 1) = 3
' Data(2, 2) = 7
' Data(3, 1) = 4
' Data(3, 2) = 8
Data = transpose2D(TwoD, 1) ' FirstIndex = 1
' Contents of Data:
' Data(1, 1) = 1
' Data(1, 2) = 5
' Data(2, 1) = 2
' Data(2, 2) = 6
' Data(3, 1) = 3
' Data(3, 2) = 7
' Data(4, 1) = 4
' Data(4, 2) = 8
End Sub
The Transpose Test
This was run on a 64bit Office version: no errors. As I recall the limit on a 32bit version was 65535 and afterward (>65535) it would raise an error.
Sub TransposeTest64bit()
Dim Data As Variant: ReDim Data(1 To 65536, 1 To 1)
Data = Application.Transpose(Data)
Debug.Print LBound(Data), UBound(Data) ' 1, 65536
ReDim Data(1 To 65537, 1 To 1)
Data = Application.Transpose(Data)
Debug.Print LBound(Data), UBound(Data) ' 1, 1
End Sub

employee sick leave table using vba

I couldn't figure out what the problem which cause "type mismatch" error when i click the command button for the first time it shows the error , when i click again it works fine. so can u help me to figure out what cause this error
Private Sub CommandButton1_Click()
Const cStart As Long = 5
Const cEnd As Long = 6
Const cDura As Long = 8
' Define Workbook.
Dim wb As Workbook: Set wb = ThisWorkbook
Dim srg As Range 'range refer to cell/multiple cells/row/column
Dim Sec As Range
Set srg = wb.Worksheets("info").Range("A2").CurrentRegion 'all data
Set Sec = wb.Worksheets("new").Range("A1").CurrentRegion
' Write values from Source Range to Data Array.
Dim Data As Variant: Data = srg.Value 'variant any type of data
Dim Dataa As Variant: Dataa = Sec.Value
Dim srCount As Long: srCount = UBound(Data, 1) ' Source Number of Rows , UBound return size of array
Dim cCount As Long: cCount = UBound(Data, 2) ' return Number of Columns
' Define Days Array.
Dim dData As Variant: ReDim dData(2 To srCount) 'array, ReDim increase number without deleting previous data
Dim countDura As Variant: ReDim countDura(srCount)
Dim rrCount As Long: rrCount = 1 ' Result Array Row Counter - 1 for headers
Dim cDiff As Long ' Days Between First and Last incl.
Dim dura As Long
Dim i As Long ' Data (Source) Array Rows
Dim d As Long
' Calculate Result Array Rows Count and populate Days Array.
For i = 2 To srCount
cDiff = DateDiff("M", Data(i, cStart), Data(i, cEnd)) + 1
dData(i) = cDiff
rrCount = rrCount + cDiff
Next i
For d = 3 To srCount
dura = Data(d, cEnd) - Data(d, cStart)
countDura(d) = dura
rrCount = rrCount + dura
Next d
' Define Result Array.
Dim Result As Variant: ReDim Result(1 To rrCount, 1 To cCount)
Dim k As Long: k = 1 ' Result Array Rows Counter - 1 for headers
' Declare additional variables.
Dim j As Long ' Data and Result Array Columns Counter
Dim n As Long ' Repeat Counter
' Write headers.
For j = 1 To cCount
Result(1, j) = Data(1, j)
Next j
' Write 'body'.
For i = 2 To srCount 'row
For n = 1 To dData(i) 'diff
k = k + 1
For j = 1 To cCount 'column
Select Case j
Case cStart 'column 5 (started date)
If dData(i) = 1 Then 'print the same dates if the diff is 1
Result(k, j) = Data(i, j)
Result(k, cEnd) = Data(i, cEnd)
Else
If n = 1 Then
Result(k, j) = Data(i, j) 'the first row of diff then change last date of month
Result(k, cEnd) = dateLastInMonth(Data(i, j))
Else
If n = dData(i) Then 'if n = diff , change first date depnd on last date(last row of diff)
Result(k, j) = dateFirstInMonth(Data(i, cEnd))
Result(k, cEnd) = Data(i, cEnd)
Result(k, cDura) = Data(i, cDura)
Else ' previous empty row
Result(k, j) = Result(k - 1, cEnd) + 1
Result(k, cEnd) = dateLastInMonth(Result(k, j))
Result(k, cDura) = Data(i, cDura)
End If
End If
End If
Case Is <> cEnd ' not equal
Result(k, j) = Data(i, j) '' print rest of data
Result(k, cDura) = Data(i, cDura)
End Select
Next j
Next n
Next i
With wb.Worksheets("new").Range("A1").Resize(, cCount)
.Resize(k).Value = Result
.Resize(.Worksheet.Rows.Count - .Row - k + 1).Offset(k).ClearContents
Dim shDest As Worksheet
Set shDest = ActiveWorkbook.Sheets("new")
Dim last_row As Long
last_row = shDest.Cells(Rows.Count, 2).End(xlUp).Row
Dim daysDura As Long
Dim countDur As Variant: ReDim countDur(last_row)
For i = 2 To last_row 'row
shDest.Cells(i, 8).ClearContents
daysDura = DateDiff("d", Dataa(i, cStart), Dataa(i, cEnd))
countDur(i) = daysDura
shDest.Cells(i, 8).Value = countDur(i)
Next i
End With
End Sub
Function dateFirstInMonth( _
ByVal d As Date) _
As Date
dateFirstInMonth = DateSerial(Year(d), Month(d), 1)
End Function
Function dateLastInMonth( _
ByVal d As Date) _
As Date
If Month(d) = 12 Then
dateLastInMonth = DateSerial(Year(d), 12, 31)
Else
dateLastInMonth = DateSerial(Year(d), Month(d) + 1, 1) - 1
End If
End Function
The code is to shows the leave dates and calculate the number of days between the dates

Table of employee sick leave in Excel by using VBA macro

I want to write code by using macro VBA which calculate the number of rows depend on the different between the leave date and to the end date of leave date , then change the row values to start from the first date of month to the end.
example:
name start_leave_date end_ leave_date
customer_1 20/3/2020 7/6/2020
customer_2 12/1/2020 15/3/2020
so the result should looks like this
name start_leave_date end_leave_date
customer_1 20/3/2020 31/3/2020
customer_1 01/4/2020 30/4/2020
customer_1 01/5/2020 31/5/2020
customer_1 01/6/2020 07/6/2020
customer_2 12/1/2020 31/1/2020
customer_2 01/2/2020 28/2/2020
customer_2 12/3/2020 31/3/2020
so there is 5 rows for customers 1 because there is different of 5 months between the start and the end of leave date
so can some one help me to know what i need to add in my code to show this output , thank you
my code and my result but it need to modify to get the output which i need
input
output
my VBA code
Private Sub CommandButton1_Click()
Dim rng As Range
Dim r As Range
Dim numberOfCopies As Integer
Dim n As Integer
Dim lastRow As Long
'Dim Lastrowa As Long
ThisWorkbook.Sheets("info").Columns("E").NumberFormat = "dd/mm/yyyy"
ThisWorkbook.Sheets("info").Columns("D").NumberFormat = "dd/mm/yyyy"
ThisWorkbook.Sheets("info").Columns("F").NumberFormat = "dd/mm/yyyy"
ThisWorkbook.Sheets("new").Columns("E").NumberFormat = "dd/mm/yyyy"
ThisWorkbook.Sheets("new").Columns("D").NumberFormat = "dd/mm/yyyy"
ThisWorkbook.Sheets("new").Columns("F").NumberFormat = "dd/mm/yyyy"
Set rng = Range("A2", Range("J1").End(xlDown))
For Each r In rng.Rows
'## Get the number of months
numberOfCopies = r.Cells(1, 11).Value
If numberOfCopies > 0 Then
'## Add to a new sheet
With Sheets("new")
'## copy the row and paste repeatedly in this loop
For n = 1 To numberOfCopies
lastRow = Sheets("new").Range("A1048576").End(xlUp).Row
r.Copy
'.Range ("A" & n)
Sheets("new").Range("A" & lastRow + 1).PasteSpecial xlPasteValues
Next
End With
End If
Next
End Sub
Unpivot Monthly
Adjust the values in the constants section.
If you don't want to copy the last column you can define the Source Range like this:
Dim srg As Range
With wb.Worksheets(sName).Range(sFirst).CurrentRegion
Set srg = .Resize(, .Columns.Count - 1)
End With
Use - 2 if you don't want the last two columns.
The Code
Option Explicit
Sub unpivotMonthly()
' Define Constants.
Const sName As String = "info"
Const sFirst As String = "A1"
Const dName As String = "new"
Const dFirst As String = "A1"
Const cStart As Long = 5
Const cEnd As Long = 6
' Define Workbook.
Dim wb As Workbook: Set wb = ThisWorkbook
' Define Source Range.
Dim srg As Range: Set srg = wb.Worksheets(sName).Range(sFirst).CurrentRegion
' Write values from Source Range to Data Array.
Dim Data As Variant: Data = srg.Value
Dim srCount As Long: srCount = UBound(Data, 1) ' Source Rows Count
Dim cCount As Long: cCount = UBound(Data, 2) ' Columns Count
' Define Months Array.
Dim mData As Variant: ReDim mData(2 To srCount)
Dim rrCount As Long: rrCount = 1 ' Result Array Rows Count - 1 for headers
Dim mDiff As Long ' Current Months Between First and Last (incl.)
Dim i As Long ' Data (Source) Array Rows Counter
' Calculate Result Array Rows Count and populate Months Array.
For i = 2 To srCount
mDiff = DateDiff("M", Data(i, cStart), Data(i, cEnd)) + 1
mData(i) = mDiff
rrCount = rrCount + mDiff
Next i
' Define Result Array.
Dim Result As Variant: ReDim Result(1 To rrCount, 1 To cCount)
Dim k As Long: k = 1 ' Result Array Rows Counter - 1 for headers
' Declare additional variables.
Dim j As Long ' Data and Result Array Columns Counter
Dim m As Long ' Months Counter
' Write headers.
For j = 1 To cCount
Result(1, j) = Data(1, j)
Next j
' Write 'body'.
For i = 2 To srCount
For m = 1 To mData(i)
k = k + 1
For j = 1 To cCount
Select Case j
Case cStart
If mData(i) = 1 Then
Result(k, j) = Data(i, j)
Result(k, cEnd) = Data(i, cEnd)
Else
If m = 1 Then
Result(k, j) = Data(i, j)
Result(k, cEnd) = dateLastInMonth(Data(i, j))
Else
If m = mData(i) Then
Result(k, j) = dateFirstInMonth(Data(i, cEnd))
Result(k, cEnd) = Data(i, cEnd)
Else
Result(k, j) = Result(k - 1, cEnd) + 1
Result(k, cEnd) = dateLastInMonth(Result(k, j))
End If
End If
End If
Case Is <> cEnd
Result(k, j) = Data(i, j)
End Select
Next j
Next m
Next i
' Write result.
With wb.Worksheets(dName).Range(dFirst).Resize(, cCount)
.Resize(k).Value = Result
.Resize(.Worksheet.Rows.Count - .Row - k + 1).Offset(k).ClearContents
End With
End Sub
Function dateFirstInMonth( _
ByVal d As Date) _
As Date
dateFirstInMonth = DateSerial(Year(d), Month(d), 1)
End Function
Function dateLastInMonth( _
ByVal d As Date) _
As Date
If Month(d) = 12 Then
dateLastInMonth = DateSerial(Year(d), 12, 31)
Else
dateLastInMonth = DateSerial(Year(d), Month(d) + 1, 1) - 1
End If
End Function
Try,
Sub test()
Dim Ws As Worksheet, toWs As Worksheet
Dim vDB, vR()
Dim sDAy As Date, eDay As Date
Dim i As Long, n As Long, r As Long
Dim j As Integer, c As Integer, k As Integer
Set Ws = Sheets(1) 'set input Sheet
Set toWs = Sheets(2) 'set ouput Sheet
vDB = Ws.Range("a1").CurrentRegion
r = UBound(vDB, 1)
ReDim vR(1 To 11, 1 To r * 20)
For i = 2 To r
sDAy = getDay(vDB(i, 5)) '<~~if Leave from is not text -> vDB(i,5)
eDay = getDay(vDB(i, 6)) '<~~if Leave to is not text -> vDB(i,6)
c = DateDiff("m", sDAy, eDay)
For j = 0 To c
n = n + 1
Select Case c
Case 0
vR(5, n) = sDAy
vR(6, n) = eDay
Case Else
If j = c Then
vR(5, n) = DateSerial(Year(sDAy), Month(sDAy) + j, 1)
vR(6, n) = eDay
ElseIf j = 0 Then
vR(5, n) = sDAy
vR(6, n) = DateSerial(Year(sDAy), Month(sDAy) + j + 1, 0)
Else
vR(5, n) = DateSerial(Year(sDAy), Month(sDAy) + j, 1)
vR(6, n) = DateSerial(Year(sDAy), Month(sDAy) + j + 1, 0)
End If
End Select
For k = 1 To 11
If k < 5 Or k > 6 Then
vR(k, n) = vDB(i, k)
If k = 4 Then
vR(k, n) = getDay(vDB(i, k)) 'if [Star work date]is not text then remove this line
End If
End If
Next k
Next j
Next i
ReDim Preserve vR(1 To 11, 1 To n)
With toWs
.Range("a1").CurrentRegion.Offset(1).ClearContents
.Range("a2").Resize(n, 11) = WorksheetFunction.Transpose(vR)
.Range("d:f").NumberFormatLocal = "dd/mm/yyyy"
End With
End Sub
Function getDay(v As Variant)
Dim vS
vS = Split(v, "/")
getDay = DateSerial(vS(2), vS(1), vS(0))
End Function

VBA: completing a matrix

I have a 3 by 3 matrix, where elements (1,1), (2,1), (2,2), (3,1), (3,2), (3,3) are given:
X . .
X X .
X X X
I need to write a program that writes out the missing elements, where (1,2)=(2,1), (1,3)=(3,1) and (2,3)=(3,2). I have written the following code:
Function kiegeszito(a)
For i = 1 To 3
For j = 1 To 3
If i < j Then
a(i, j) = a(j, i)
Else
a(i, j) = a(i, j)
End If
Next j
Next i
kiegeszito = a
End Function
However, this does not seem to work, could anybody help me why is this not working?
Just remove the Else condition:
Function kiegeszito(a)
For i = 1 To 3
For j = 1 To 3
If i < j Then a(i, j) = a(j, i)
Next j
Next i
kiegeszito = a
End Function
Get twin data in 2-dim matrix avoiding extra n*(n-1)/2 condition checks
The following approach
reduces the number of unnecessary condition checks by incrementing the 2nd loop starts
accepts any wanted base of 2-dim data:
Sub CompleteMatrix(ByRef data)
'count row|=column elements
Dim cnt As Long: cnt = UBound(data) - LBound(data) + 1
'fill missing twin data (identified by inverted indices)
Dim i As Long, j As Long
For i = LBound(data) To cnt - 1
'~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
'next column starts from incremented row index
'(thus avoiding n*(n-1)/2 IF-conditions)
'~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
For j = i + 1 To UBound(data, 2)
data(i, j) = data(j, i) ' assign twin data
Next j
Next i
End Sub
An example call creating e.g. a 1-based 2-dim datafield array might be
Sub ExampleCall()
Dim v: v = Tabelle3.Range("A1:C3").Value
CompleteMatrix v
End Sub
Further link
A practical example using such a mirrored array might be a distance array; a related post demonstrates how to apply the FilterXML() function thereon.
Fill Array
Using a method (fillArray) you could modify the array 'in place':
The Code
Option Explicit
Sub fillArrayTEST()
Dim Data As Variant: Data = Range("A1:C3").Value
debugPrint2D Data
fillArray Data
debugPrint2D Data
End Sub
Sub fillArray(ByRef Data As Variant)
Dim cCount As Long: cCount = UBound(Data, 2)
Dim i As Long, j As Long
For i = 1 To UBound(Data, 1)
For j = 1 To cCount
If i < j Then Data(i, j) = Data(j, i)
Next j
Next i
End Sub
Sub debugPrint2D(ByVal Data As Variant)
Dim i As Long, j As Long
For i = LBound(Data, 1) To UBound(Data, 1)
For j = LBound(Data, 2) To UBound(Data, 2)
Debug.Print "[" & i & "," & j & "]", Data(i, j)
Next j
Next i
End Sub
A Homage to T.M.'s Brilliant Solution
Sub completeMatrix(ByRef Data As Variant)
Dim rLower As Long: rLower = LBound(Data, 1)
Dim cLower As Long: cLower = LBound(Data, 2)
Dim iDiff As Long: iDiff = cLower - rLower
Dim cStart As Long: cStart = iDiff + 1
Dim cUpper As Long: cUpper = UBound(Data, 2)
Dim r As Long, c As Long
For r = rLower To UBound(Data, 1) - rLower
For c = cStart + r To cUpper
Data(r, c) = Data(c - iDiff, r + iDiff)
Next c
Next r
End Sub
Sub completeMatrixTEST()
Dim Data As Variant: ReDim Data(0 To 2, 2 To 4)
Data(0, 2) = 1
Data(1, 2) = 2
Data(1, 3) = 3
Data(2, 2) = 4
Data(2, 3) = 5
Data(2, 4) = 6
debugPrint2D Data
completeMatrix Data
'Range("G1").Resize(UBound(Data, 1) - LBound(Data, 1) + 1, _
UBound(Data, 2) - LBound(Data, 2) + 1).Value = Data
Debug.Print
debugPrint2D Data
End Sub

Resources