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
Related
i can't find where i am doing wrong my code is not working so. I'm a bit of a novice at this, I don't quite understand what the problem is
it gives me warning on this line
matrix = Range("B5").Resize(rows, cols)
Sub TamsayiliRasgeleMatris()
'Deklarasyonlar
Dim rows As Integer, cols As Integer
Dim lowerBound As Integer, upperBound As Integer
Dim sum As Double, average As Double
'Kullanıcıdan girdiler alma
rows = Range("A2").Value
cols = Range("B2").Value
lowerBound = Range("C2").Value
upperBound = Range("D2").Value
'Boş bir matris oluşturma
Dim matrix As Variant
matrix = Range("B5").Resize(rows, cols)
'Matrisi rastgele sayılarla doldurma
For i = 1 To rows
For j = 1 To cols
matrix(i, j) = Int((upperBound - lowerBound + 1) * Rnd + lowerBound)
sum = sum + matrix(i, j)
Next j
Next i
'Matrisi çalışma sayfasına yazma
matrix.Copy Destination:=Range("B5")
'Ortalama değerini hesaplayın ve E2 hücresine yazma
average = sum / (rows * cols)
Range("E2").Value = average
'Matris transpozunu oluşturun ve altına yapıştırın
Dim transposed As Variant
transposed = Application.Transpose(matrix)
transposed.Copy Destination:=Range("B5").Offset(rows + 1, 0)
'Değerleri ortalama değerine göre renklendirin
For i = 1 To rows
For j = 1 To cols
If matrix(i, j) < average Then
matrix(i, j).Interior.Color = vbCyan
ElseIf matrix(i, j) > average Then
matrix(i, j).Interior.Color = vbMagenta
End If
Next j
Next i
End Sub
Understanding Ranges and Arrays
A lot was changed so some of your comments may not apply anymore.
Option Explicit
Sub TamsayiliRasgeleMatris()
Dim ws As Worksheet: Set ws = ActiveSheet ' Improve!
'Kullanicidan girdiler alma
Dim rCount As Long: rCount = ws.Range("A2").Value
Dim cCount As Long: cCount = ws.Range("B2").Value
Dim MinInteger As Long: MinInteger = ws.Range("C2").Value
Dim MaxInteger As Long: MaxInteger = ws.Range("D2").Value
'Boş bir matris oluşturma
Dim Matrix() As Variant: ReDim Matrix(1 To rCount, 1 To cCount)
Dim r As Long, c As Long, Total As Long
'Matrisi rastgele sayilarla doldurma
For r = 1 To rCount
For c = 1 To cCount
Matrix(r, c) = Int((MaxInteger - MinInteger + 1) * Rnd + MinInteger)
Total = Total + Matrix(r, c)
Next c
Next r
ws.Range("E2").Value = Total
Dim rg As Range, fCell As Range
'Matrisi çalişma sayfasina yazma
Set fCell = ws.Range("B5")
With fCell
.Resize(ws.Rows.Count - .Row + 1, ws.Columns.Count - .Column + 1).Clear
End With
Set rg = fCell.Resize(rCount, cCount)
rg.Value = Matrix
'Ortalama degerini hesaplayin ve F2 hücresine yazma
Dim Avg As Double: Avg = Total / (rCount * cCount)
ws.Range("F2").Value = Avg
'Degerleri ortalama degerine göre renklendirin
For r = 1 To rCount
For c = 1 To cCount
Select Case Matrix(r, c)
Case Is < Avg: rg.Cells(r, c).Interior.Color = vbCyan
Case Is > Avg: rg.Cells(r, c).Interior.Color = vbMagenta
Case Else ' !?
End Select
Next c
Next r
'Matris transpozunu oluşturun ve altina yapiştirin
Dim tMatrix() As Long: ReDim tMatrix(1 To cCount, 1 To rCount)
For r = 1 To rCount
For c = 1 To cCount
tMatrix(c, r) = Matrix(r, c)
Next c
Next r
Set fCell = fCell.Offset(rCount + 1)
Set rg = fCell.Resize(cCount, rCount)
rg.Value = tMatrix
'Degerleri ortalama degerine göre renklendirin
For c = 1 To cCount
For r = 1 To rCount
Select Case tMatrix(c, r)
Case Is < Avg: rg.Cells(c, r).Interior.Color = vbCyan
Case Is > Avg: rg.Cells(c, r).Interior.Color = vbMagenta
Case Else ' !?
End Select
Next r
Next c
End Sub
Here follow some suggestion to possibly make your code run
taking in consideration the following code snippet:
Dim matrix As Variant
matrix = Range("B5").Resize(rows, cols)
since:
matrix is declared as of Variant type
Value is the default property for any Range object
then matrix is finally resulting in a Variant array, as if you had coded:
matrix = Range("B5").Resize(rows, cols).Value
further on you are coding:
matrix.Copy Destination:=Range("B5")
which would result in an error since an array doesn't have any Copy method, while this latter is available for many objects, among which the Range object
hence you should sort of "reverse" the matrix assignation code line as follows:
'Matrisi çalisma sayfasina yazma
Range("B5").Resize(rows, cols).Value = matrix
just a little more complicated is the fix of the other wrong Copy statement
Dim transposed As Variant
transposed = Application.Transpose(matrix)
transposed.Copy Destination:=Range("B5").Offset(rows + 1, 0)
which, along the lines of the preceeding fix, is to be coded as follows:
Dim transposed As Variant
transposed = Application.Transpose(matrix)
Range("B5").Offset(rows + 1, 0).Resize(cols, rows).Value = transposed
and where you'll notice I swapped cols and rows in the Resize() property to account for transposition
finally the following snippet:
'Değerleri ortalama değerine göre renklendirin
For i = 1 To rows
For j = 1 To cols
If matrix(i, j) < average Then
matrix(i, j).Interior.Color = vbCyan
ElseIf matrix(i, j) > average Then
matrix(i, j).Interior.Color = vbMagenta
End If
Next j
Next i
is to be twicked as follows:
With Range("B5") 'reference the target range upper-left cell
For i = 1 To rows
For j = 1 To cols
If matrix(i, j) < average Then
.Offset(i - 1, j - 1).Interior.Color = vbCyan 'write in the cell corresponding to the ith row and jth column of matrix
ElseIf matrix(i, j) > average Then
.Offset(i - 1, j - 1).Interior.Color = vbMagenta
End If
Next
Next
End With
The code I have takes cells containing the delimiter (; ) from a column, and creates new rows (everything except the column is duplicated) to separate those values.
What I have
I need this for multiple columns in my data, but I don't want the data to overlap (ex: for 3 columns, I want there to be only one value per row in those 3 columns). It would be ideal if I could select multiple columns instead of only one as my code does now.
What I want
Sub splitByCol()
Dim r As Range, i As Long, ar
Set r = Worksheets("Sheet").Range("J2000").End(xlUp)
Do While r.Row > 1
ar = Split(r.Value, "; ")
If UBound(ar) >= 0 Then r.Value = ar(0)
For i = UBound(ar) To 1 Step -1
r.EntireRow.Copy
r.Offset(1).EntireRow.Insert
r.Offset(1).Value = ar(i)
Next
Set r = r.Offset(-1)
Loop
End Sub
Try this code
Sub Test()
Dim a, x, e, i As Long, ii As Long, iii As Long, k As Long
a = Range("A1").CurrentRegion.Value
ReDim b(1 To 1000, 1 To UBound(a, 2))
For i = LBound(a) To UBound(a)
For ii = 2 To 3
x = Split(a(i, ii), "; ")
For Each e In x
k = k + 1
b(k, 1) = k
b(k, 2) = IIf(ii = 2, e, Empty)
b(k, 3) = IIf(ii = 3, e, Empty)
b(k, 4) = a(i, 4)
Next e
Next ii
Next i
Range("A5").Resize(UBound(b, 1), UBound(b, 2)).Value = b
End Sub
I'd go this way
Sub SplitByCol()
With Worksheets("Sheet")
With .Range("B2", .Cells(.Rows.Count, "B").End(xlUp))
Dim firstColValues As Variant
firstColValues = .Value
Dim secondColValues As Variant
secondColValues = .Offset(, 1).Value
Dim thirdColValues As Variant
thirdColValues = .Offset(, 2).Value
.Offset(, -1).Resize(, 4).ClearContents
End With
Dim iRow As Long
For iRow = LBound(firstColValues) To UBound(firstColValues)
Dim currFirstColValues As Variant
currFirstColValues = Split(firstColValues(iRow, 1), "; ")
Dim currSecondColValues As Variant
currSecondColValues = Split(secondColValues(iRow, 1), "; ")
With .Cells(.Rows.Count, "C").End(xlUp).Offset(1, -1)
With .Resize(UBound(currFirstColValues) + 1)
.Value = currFirstColValues
.Offset(, 2).Value = thirdColValues(iRow, 1)
End With
End With
With .Cells(.Rows.Count, "B").End(xlUp).Offset(1, 1)
With .Resize(UBound(currSecondColValues) + 1)
.Value = currSecondColValues
.Offset(, 1).Value = thirdColValues(iRow, 1)
End With
End With
Next
End With
End Sub
Follow the code step by step by pressing F8 while the cursor is in any code line in the VBA IDE and watch what happens in the Excel user interface
EDIT
adding edited code for a more "parametric" handling by means of a helper function
Sub SplitByCol()
With Worksheets("Sheet")
With .Range("B2", .Cells(.Rows.Count, "B").End(xlUp))
Dim firstColValues As Variant
firstColValues = .Value
Dim secondColValues As Variant
secondColValues = .Offset(, 1).Value
Dim thirdColValues As Variant
thirdColValues = .Offset(, 2).Value
.Offset(, -1).Resize(, 4).ClearContents
End With
Dim iRow As Long
For iRow = LBound(firstColValues) To UBound(firstColValues)
Dim currFirstColValues As Variant
currFirstColValues = Split(firstColValues(iRow, 1), "; ")
Dim currSecondColValues As Variant
currSecondColValues = Split(secondColValues(iRow, 1), "; ")
WriteOne .Cells(.Rows.Count, "C").End(xlUp).Offset(1), _
currFirstColValues, thirdColValues(iRow, 1), _
-1, 2
WriteOne .Cells(.Rows.Count, "B").End(xlUp).Offset(1), _
currSecondColValues, thirdColValues(iRow, 1), _
1, 1
Next
End With
End Sub
Sub WriteOne(refCel As Range, _
currMainColValues As Variant, thirdColValue As Variant, _
mainValuesOffsetFromRefCel As Long, thirdColValuesOffsetFromRefCel As Long)
With refCel.Offset(, mainValuesOffsetFromRefCel)
With .Resize(UBound(currMainColValues) + 1)
.Value = currMainColValues
.Offset(, thirdColValuesOffsetFromRefCel).Value = thirdColValue
End With
End With
End Sub
Please, use the next code. It uses arrays and should be very fast for big ranges to be processed, working mostly in memory:
Sub testSplitInsert()
Dim sh As Worksheet, lastR As Long, arr, arrSp, arrFin, i As Long, j As Long, k As Long
Set sh = ActiveSheet
lastR = sh.Range("B" & sh.rows.count).End(xlUp).row
arr = sh.Range("B1:D" & lastR).Value
ReDim arrFin(1 To UBound(arr) * 10, 1 To 3) 'maximum to keep max 10 rows per each case
k = 1 'initialize the variable to load the final array
For i = 1 To UBound(arr)
arrSp = Split(Replace(arr(i, 1)," ",""), ";") 'trim for the case when somebody used Red;Blue, instead of Red; Blue
For j = 0 To UBound(arrSp)
arrFin(k, 1) = arrSp(j): arrFin(k, 3) = arr(i, 3): k = k + 1
Next j
arrSp = Split(Replace(arr(i, 1)," ",""), ";")
For j = 0 To UBound(arrSp)
arrFin(k, 2) = arrSp(j): arrFin(k, 3) = arr(i, 3): k = k + 1
Next j
Next
sh.Range("G1").Resize(k - 1, 3).Value = arrFin
End Sub
It processes the range in columns "B:D" and returns the result in columns "G:I". It can be easily adapted to process any columns range and return even overwriting the existing range, but this should be done only after checking that it return what you need...
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
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
I am using Excel 2016 and I am new to VBA. I have an Excel worksheet which contains 262 rows (with no headers). An extract of the first 2 rows are shown below (starts at column A and ends at column L):
I would like to run a VBA code on the worksheet to transpose the data as follows:
How should I go about it?
Try
Sub test()
Dim vDB, vR()
Dim i As Long, j As Integer, n As Long
Dim r As Long
vDB = Range("a1").CurrentRegion
r = UBound(vDB, 1)
For i = 1 To r
For j = 1 To 6
n = n + 1
ReDim Preserve vR(1 To 2, 1 To n)
vR(1, n) = vDB(i, j)
vR(2, n) = vDB(i, j + 6)
Next j
Next i
Sheets.Add
Range("a1").Resize(n, 2) = WorksheetFunction.Transpose(vR)
End Sub
A Special Transpose
Sub SpecialTranspose()
Const cLngRows As Long = 262 ' Source Number of Rows
Const cIntColumns As Integer = 6 ' Source Number of Columns Per Set
Const cIntSets As Integer = 2 ' Source Number of Sets
Const cStrSourceCell As String = "A1" ' Source First Cell
Const cStrTargetCell = "M1" ' Target First Cell
Dim vntSource As Variant ' Source Array
Dim vntTarget As Variant ' Target Array
Dim h As Integer ' Source Array Set Counter / Target Array Column Counter
Dim i As Long ' Source Array Row Counter
Dim j As Integer ' Source Array Column Counter
Dim k As Long ' Target Array Row Counter
' Resize Source First Cell to Source Range and paste it into Source Array.
vntSource = Range(cStrSourceCell).Resize(cLngRows, cIntColumns * cIntSets)
' Resize Target Array
ReDim vntTarget(1 To cLngRows * cIntColumns, 1 To cIntSets)
' Calculate and write data to Target Array.
For h = 1 To cIntSets
For i = 1 To cLngRows
For j = 1 To cIntColumns
k = k + 1
vntTarget(k, h) = vntSource(i, cIntColumns * (h - 1) + j)
Next
Next
k = 0
Next
' Paste Target Array into Target Range resized from Target First Cell.
Range(cStrTargetCell).Resize(cLngRows * cIntColumns, cIntSets) = vntTarget
End Sub
You could use arrays to do your transpose:
Sub Transpose()
'Declare variables
Dim wsHome As Worksheet
Dim arrHome, arrNumber(), arrLetter() As Variant
Dim intNum, intLetter, lr, lc As Integer
Set wsHome = ThisWorkbook.Worksheets("Sheet1")
Set collNumber = New Collection
Set collLetter = New Collection
'Set arrays to position to 0
intNum = 0
intLetter = 0
'Finds last row and column of data
lc = Cells(1, Columns.Count).End(xlToLeft).Column
lr = Cells(Rows.Count, 1).End(xlUp).Row
'Move data into array
arrHome = wsHome.Range(Cells(1, 1), Cells(lr, lc)).Value
For x = LBound(arrHome, 1) To UBound(arrHome, 1)
For y = LBound(arrHome, 2) To UBound(arrHome, 2)
'Check if value is numeric
If IsNumeric(arrHome(x, y)) = True Then
ReDim Preserve arrNumber(intNum)
arrNumber(intNum) = arrHome(x, y)
intNum = intNum + (1)
Else
ReDim Preserve arrLetter(intLetter)
arrLetter(intLetter) = arrHome(x, y)
intLetter = intLetter + 1
End If
Next y
Next x
'clear all values in sheet
wsHome.UsedRange.ClearContents
ActiveSheet.Range("A1").Resize(UBound(arrNumber), 1).Value = Application.WorksheetFunction.Transpose(arrNumber)
ActiveSheet.Range("B1").Resize(UBound(arrLetter), 1).Value = Application.WorksheetFunction.Transpose(arrLetter)
End Sub
Let us assume that data appears in Sheet 1.Try:
Option Explicit
Sub TEST()
Dim LastColumn As Long, LastRowList As Long, LastRowNumeric As Long, LastRowNonNumeric As Long, R As Long, C As Long
With ThisWorkbook.Worksheets("Sheet1")
LastRowList = .cells(.Rows.Count, "A").End(xlUp).Row
LastColumn = .cells(1, .Columns.Count).End(xlToLeft).Column
For R = 1 To LastRowList
For C = 1 To LastColumn
If IsNumeric(.cells(R, C).Value) = True Then
LastRowNumeric = .cells(.Rows.Count, LastColumn + 2).End(xlUp).Row
If LastRowNumeric = 1 And .cells(1, LastColumn + 2).Value = "" Then
.cells(LastRowNumeric, LastColumn + 2).Value = .cells(R, C).Value
Else
.cells(LastRowNumeric + 1, LastColumn + 2).Value = .cells(R, C).Value
End If
Else
LastRowNonNumeric = .cells(.Rows.Count, LastColumn + 3).End(xlUp).Row
If LastRowNonNumeric = 1 And .cells(1, LastColumn + 3).Value = "" Then
.cells(LastRowNonNumeric, LastColumn + 3).Value = .cells(R, C).Value
Else
.cells(LastRowNonNumeric + 1, LastColumn + 3).Value = .cells(R, C).Value
End If
End If
Next C
Next R
End With
End Sub