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
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
I am trying to remove duplicates but with case sensitivity.
For example, ABC123 is not the same as abc123, hence, do not remove it.
But ABC123 and ABC123 is the same, hence, remove them.
This is my current code:
Dim oDic As Object, vData As Variant, r As Long
Set oDic = CreateObject("Scripting.Dictionary")
With worksheets(4).Range("A7:A" & lastRow)
vData = .Value
.ClearContents
End With
With oDic
.comparemode = 0
For r = 1 To UBound(vData, 1)
If Not IsEmpty(vData(r, 1)) And Not .Exists(vData(r, 1)) Then
.Add vData(r, 1), Nothing
End If
Next r
Range("A7").Resize(.Count) = Application.Transpose(.keys)
End With
Some background:
The entire dataset has about 800k records
The script has no error, but the result is wrong. When I remove duplicate (regardless of case sensitivity, I have 400k left) but running this script, 450k (which sounds legit), but only 60k records have data, 390k shows #N/A. So I have no idea where went wrong.
Thanks in advance!
As stated in the first comment, Application.Transpose has a limitation of 65,536 array rows. Please, try the next function able to transpose without such a limitation:
Function TranspKeys(arrK) As Variant
Dim arr, i As Long
ReDim arr(1 To UBound(arrK) + 1, 1 To 1)
For i = 0 To UBound(arrK)
arr(i + 1, 1) = arrK(i)
Next i
TranspKeys = arr
End Function
After copying the functionin the same module where your existing code exists, only modify it as:
Range("A7").Resize(.Count,1) = TranspKeys(.keys)
Unique Values Case-Sensitive
Transpose has its limitations and is best avoided (what's a few more lines).
Option Explicit
Sub DictWith()
With Worksheets(4)
Dim LastRow As Long: LastRow = .Cells(.Rows.Count, "A").End(xlUp).Row
If LastRow < 7 Then Exit Sub
With .Range("A7:A" & LastRow)
Dim Data As Variant
If .Rows.Count = 1 Then
ReDim Data(1 To 1, 1 To 1)
Data(1, 1).Value = .Value
Else
Data = .Value
End If
With CreateObject("Scripting.Dictionary")
.CompareMode = vbBinaryCompare
Dim Key As Variant
Dim r As Long
For r = 1 To UBound(Data, 1)
Key = Data(r, 1)
If Not IsError(Key) Then
If Len(Key) > 0 Then
.Item(Key) = Empty
End If
End If
Next r
Dim rCount As Long: rCount = .Count
If rCount = 0 Then Exit Sub
ReDim Data(1 To rCount, 1 To 1)
r = 0
For Each Key In .Keys
r = r + 1
Data(r, 1) = Key
Next Key
End With
.Resize(rCount).Value = Data
.Resize(.Worksheet.Rows.Count - .Row - rCount + 1) _
.Offset(rCount).ClearContents ' clear below
End With
End With
End Sub
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
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
I am trying to write a simple randomizing program that reads from a column of names and randomly writes them to three columns of four. I have something that kind of works, but it is duplicating my names and I can figure out how to fix it with arrays or collections as those wont let me compare values. Thank you in advance.
Goal: randomization without doubling up two names
Problem: comparing and writing (to worksheet) collection and/or arrays
Option Explicit
Private Sub Randomize_Click()
Dim HowMany As Integer
Dim NoOfNames As Long
Dim RandomNumber As Integer
Dim Names(), dub() As String 'Array to store randomly selected names
Dim i, j, r, a, p As Byte
Dim CellsOut As Long 'Variable to be used when entering names onto worksheet
Dim ArI As Byte 'Variable to increment through array indexes
Application.ScreenUpdating = False
HowMany = 4 ' use with a third loops?
CellsOut = 4
For a = 1 To 6
For r = 1 To 3
For j = 2 To 5
'CellsOut = i 'turn this into loops
ReDim Names(1 To 4) 'Set the array size to how many names required
NoOfNames = Application.CountA(Worksheets("Employees").Range("A:A")) - 1 ' Find how many
names in the list
i = 1
Do While i <= HowMany
RandomNo:
RandomNumber = Application.RandBetween(2, NoOfNames + 1)
dub = RandomNumber
'dub.Add Unit.Value
If Names(i) = Cells(RandomNumber, 1).Value Then
'If Names(i) = dub(Unit) Then
GoTo RandomNo
End If
Names(i) = Worksheets("Employees").Cells(RandomNumber, 1).Value ' Assign random
name to the array
i = i + 1 '
Loop
'Loop through the array and enter names onto the worksheet
For ArI = LBound(Names) To UBound(Names)
Cells(CellsOut, j) = Names(ArI)
CellsOut = CellsOut + 1
Next ArI
Application.ScreenUpdating = True
CellsOut = 4
Next j
Next r
Next a
End Sub
Display
Names
Random Names
Current Setup
This solution uses the dictionary to randomize numbers which I was exploring earlier today.
The complete code goes into a standard module.
Adjust the three constants at the beginning of randomizeNames.
You only run randomizeNames, e.g. via a command button:
Private Sub Randomize_Click()
randomizeNames
End Sub
The Code
Option Explicit
Sub randomizeNames()
' Constants
Const srcFirst As String = "A2"
Const NoC As Long = 3
Const tgtFirst As String = "C2"
' Define Source First Cell Range ('cel').
Dim cel As Range
Set cel = Range(srcFirst)
' Define Source Last Cell Range ('rng').
Dim rng As Range
Set rng = Cells(Rows.Count, cel.Column).End(xlUp)
' Define Source Column Range ('rng').
Set rng = Range(cel, rng)
' Define Number of Elements (names) ('NoE').
Dim NoE As Long
NoE = rng.Rows.Count
' Write values from Source Column Range to Source Array ('Source').
Dim Source As Variant
If NoE > 1 Then
Source = rng.Value
Else
ReDim Source(1 To 1, 1 To 1)
Source(1, 1) = rng.Value
End If
' Define Random Numbers Array ('RNA').
Dim RNA As Variant
' This line uses both functions.
RNA = getDictionary(Dictionary:=getRandomDictionary(1, NoE), _
FirstOnly:=True)
' Instead of numbers, write elements from Source Array
' to Random Number Array (Random Names Array).
Dim i As Long
For i = 1 To NoE
RNA(i, 1) = Source(RNA(i, 1), 1)
Next i
' Define Number of Rows in Target Array ('NoR') and the Remainder
' of elements ('Remainder').
Dim NoR As Long
NoR = Int(NoE / NoC)
Dim Remainder As Long
Remainder = NoE Mod NoC
If Remainder > 0 Then
NoR = NoR + 1
Else
Remainder = NoC
End If
' Define Target Array ('Target').
Dim Target As Variant
ReDim Target(1 To NoR, 1 To NoC)
' Declare additional variables.
Dim j As Long ' Target Array Columns Counter
Dim k As Long ' Random Names Array Rows Counter
' Write values from Random Names Array to Target Array.
For i = 1 To NoR - 1
For j = 1 To NoC
k = k + 1
Target(i, j) = RNA(k, 1)
Next j
Next i
For j = 1 To Remainder
k = k + 1
Target(i, j) = RNA(k, 1)
Next j
' Define Target First Cell Range ('cel').
Set cel = Range(tgtFirst)
' Clear contents from Target First Cell Range to bottom-most cell
' of last column of Target Range.
cel.Resize(Rows.Count - cel.Row + 1, NoC).ClearContents
' Write values from Target Array to Target Range.
Range(tgtFirst).Resize(NoR, NoC).Value = Target
End Sub
Function getRandomDictionary(ByVal LowOrHigh As Long, _
ByVal HighOrLow As Long) _
As Object
' Define Numbers Dictionary ('dict').
Dim dict As Object
Set dict = CreateObject("Scripting.Dictionary")
' Define the lower number ('Minimum') and the Number of Elements ('NoE').
Dim NoE As Long
Dim Minimum As Long
If LowOrHigh < HighOrLow Then
Minimum = LowOrHigh
NoE = HighOrLow - LowOrHigh + 1
Else
Minimum = HighOrLow
NoE = LowOrHigh - HighOrLow + 1
End If
' Write random list of numbers to Numbers Dictionary.
Dim Current As Long
Do
' Randomize ' Takes considerably longer.
Current = Int(Minimum + NoE * Rnd)
dict(Current) = Empty
Loop Until dict.Count = NoE
' Write result.
Set getRandomDictionary = dict
End Function
Function getDictionary(Dictionary As Object, _
Optional ByVal Horizontal As Boolean = False, _
Optional ByVal FirstOnly As Boolean = False, _
Optional ByVal Flip As Boolean = False) _
As Variant
' Validate Dictionary.
If Dictionary Is Nothing Then
GoTo ProcExit
End If
Dim NoE As Long
NoE = Dictionary.Count
If NoE = 0 Then
GoTo ProcExit
End If
' Write values from Dictionary to Data Array ('Data').
Dim Data As Variant
Dim Key As Variant
Dim i As Long
If Not Horizontal Then
If Not FirstOnly Then
ReDim Data(1 To NoE, 1 To 2)
If Not Flip Then
For Each Key In Dictionary.Keys
i = i + 1
Data(i, 1) = Key
Data(i, 2) = Dictionary(Key)
Next Key
Else
For Each Key In Dictionary.Keys
i = i + 1
Data(i, 1) = Dictionary(Key)
Data(i, 2) = Key
Next Key
End If
Else
ReDim Data(1 To NoE, 1 To 1)
If Not Flip Then
For Each Key In Dictionary.Keys
i = i + 1
Data(i, 1) = Key
Next Key
Else
For Each Key In Dictionary.Keys
i = i + 1
Data(i, 1) = Dictionary(Key)
Next Key
End If
End If
Else
If Not FirstOnly Then
ReDim Data(1 To 2, 1 To NoE)
If Not Flip Then
For Each Key In Dictionary.Keys
i = i + 1
Data(1, i) = Key
Data(2, i) = Dictionary(Key)
Next Key
Else
For Each Key In Dictionary.Keys
i = i + 1
Data(1, i) = Dictionary(Key)
Data(2, i) = Key
Next Key
End If
Else
ReDim Data(1 To 1, 1 To NoE)
If Not Flip Then
For Each Key In Dictionary.Keys
i = i + 1
Data(1, i) = Key
Next Key
Else
For Each Key In Dictionary.Keys
i = i + 1
Data(1, i) = Dictionary(Key)
Next Key
End If
End If
End If
' Write result.
getDictionary = Data
ProcExit:
End Function
List of US Top 30 Names
James
John
Robert
Michael
William
Mary
David
Joseph
Richard
Charles
Thomas
Christopher
Daniel
Elizabeth
Matthew
Patricia
George
Jennifer
Linda
Anthony
Barbara
Donald
Paul
Mark
Andrew
Edward
Steven
Kenneth
Margaret
Joshua