Round up to next whole number - excel

I have data in sheet1 (master sheet) to be divided by 300 and then pasted in every sheet.
These sheets are created by a VBA code for loop.
If my lastrow gives a decimal value I found problems.
e.g. I calculate the Last Row of MYdata and divide it by 300 as I need data in every sheet max 300. If my lastrow data is 1342, dividing by 300 I get 4.473333333. This creates only 4 sheets. I require 5 sheets of data.
Sub test()
Dim lrow As Long, lrow1 As Double
lrow = Sheet3.Range("a65000").End(xlUp).Row
lrow1 = Sheet3.Range("a65000").End(xlUp).Row
num = lrow / 300
For x = 1 To num
Sheets.Add
ActiveSheet.Name = x
Sheet3.Activate
Sheet3.Range("a2:a301").Select
Sheet3.Range("a2:a301").Cut
Sheets(x).Select Range("a1").Select
ActiveSheet.Paste
Sheet3.Select
Selection.Delete Shift:=xlUp
Next x
End Sub

This will always round you to the next whole number
num = Round((lrow / 300) + 0.5, 1)
Some suggested updates
Option Explicit
Sub Parse ()
Dim ws As Worksheet: Set ws = ThisWorkbook.Sheets("Sheet3")
Dim ns As Worksheet 'New Sheet
Dim lrow As Long, x
lrow = ws.Range("A" & ws.Rows.Count).End(xlUp).Row
For x = 1 To Round((lrow / 300) + 0.5, 1)
Set ns = ThisWorkbook.Sheets.Add(After:=ThisWorkbook.Sheets(ThisWorkbook.Sheets.Count))
ns.Name = x
ws.Range("A2:A301").Copy ns.Range("A1")
ws.Range("A2:A301").Delete Shift:=xlUp
Next x
End Sub

Related

VBA - How do I randomly select 10% of rows from a column, ensuring they are different and put a Y in column B?

I am looking to randomly select 10% of tasks worked by different users ('originator' Column P) and place a Y in column B to allow checkers to QC the work. If the 10% is not a whole number then I am required to round up i.e. 0.8 would require 1 row and 1.3 would require 2 rows.
I am new to coding I have been able to add code to filter the rows to show the required date and the 'Originator' in column P then name this range as "userNames". I am not sure how to code to select the random 10%. I have changed the part I am struggling with to bold below.
Sub randomSelection()
Dim dt As Date
dt = "20/08/2021"
Dim lRow As Long
'Format date
Range("J:J").Select
Selection.NumberFormat = "dd/mm/yyyy"
'Select User Grogu
ActiveSheet.Range("$A$1:$W$10000").AutoFilter 10, Criteria1:="=" & dt
ActiveSheet.Range("$A$1:$W$10000").AutoFilter Field:=16, Criteria1:= _
"SW\Grogu"
'Name range "userNames"
With ActiveSheet
lRow = .Cells(Rows.Count, 16).End(xlUp).Row
If lRow < 3 Then Exit Sub
.Cells(1, 16).Offset(1, 0).Resize(lRow - 1).SpecialCells(xlCellTypeVisible).Select
End With
Selection.Name = "userNames"
**'Randomly select 10% of rows from originator and put a Y in column B**
'remove all defined names
On Error Resume Next
ActiveWorkbook.Names("userNames").Delete
'Select User Finn
ActiveSheet.Range("$A$1:$W$10000").AutoFilter 10, Criteria1:="=" & dt
ActiveSheet.Range("$A$1:$W$10000").AutoFilter Field:=16, Criteria1:= _
"SW\Finn"
'Name range "userNames"
With ActiveSheet
lRow = .Cells(Rows.Count, 16).End(xlUp).Row
If lRow < 3 Then Exit Sub
.Cells(1, 16).Offset(1, 0).Resize(lRow - 1).SpecialCells(xlCellTypeVisible).Select
End With
Selection.Name = "userNames"
'remove all defined names
On Error Resume Next
ActiveWorkbook.Names("userNames").Delete
'Formate Date back
Range("J:J").Select
Selection.NumberFormat = "yyyy-mm-dd"
End Sub
I had some free time and wrote up an example program that copies 10% of a defined set of rows, and then pastes it into a different sheet. I have added some comments to help explain what each section is achieving.
Sub Example()
'Define the Start and End of the data range
Const STARTROW As Long = 1
Dim LastRow As Long
LastRow = Sheet1.Cells(Sheet1.Rows.Count, 1).End(xlUp).Row
'Create an Array - Length = Number of Rows in the data
Dim RowArr() As Long
ReDim RowArr(STARTROW To LastRow)
'Fill the Array - Each element is a row #
Dim i As Long
For i = LBound(RowArr) To UBound(RowArr)
RowArr(i) = i
Next i
'Shuffle the Row #'s within the Array
Randomize
Dim tmp As Long, RndNum As Long
For i = LBound(RowArr) To UBound(RowArr)
RndNum = WorksheetFunction.Floor((UBound(RowArr) - LBound(RowArr) + 1) * Rnd, 1) + LBound(RowArr)
tmp = RowArr(i)
RowArr(i) = RowArr(RndNum)
RowArr(RndNum) = tmp
Next i
'Calculate the number of rows to divvy up
Const LIMIT As Double = 0.1 '10%
Dim Size As Long
Size = WorksheetFunction.Ceiling((UBound(RowArr) - LBound(RowArr) + 1) * LIMIT, 1)
If Size > UBound(RowArr) Then Size = UBound(RowArr)
'Collect the chosen rows into a range
Dim TargetRows As Range
For i = LBound(RowArr) To LBound(RowArr) + Size
If TargetRows Is Nothing Then
Set TargetRows = Sheet1.Rows(RowArr(i))
Else
Set TargetRows = Union(TargetRows, Sheet1.Rows(RowArr(i)))
End If
Next i
'Define the Output Location
Dim OutPutRange As Range
Set OutPutRange = Sheet2.Cells(1, 1) 'Top Left Corner
'Copy the randomly chosen rows to the output location
TargetRows.Copy Destination:=OutPutRange.Resize(TargetRows.Rows.Count).EntireRow
End Sub

Array of filtered data to populate ListBox

Okay so I am filtering a sheet ("Data") by a criteria:
Sub Filter_Offene()
Sheets("Data").Range("A:R").AutoFilter Field:=18, Criteria1:="WAHR"
End Sub
Then, I want to put the Filtered Table to populate a Listbox
My problem here is, that the amount of rows can vary, so I thought i could try and list where the filtered table "ends" by doing this cells.find routine:
Dim lRow As Long
Dim lCol As Long
lRow = ThisWorkbook.Sheets("Data").Cells.Find(What:="*", _
After:=Range("A1"), _
LookAt:=xlPart, _
LookIn:=xlFormulas, _
SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious, _
MatchCase:=False).Row
lRow = lRow + 1
This unfotunatly also counts "hidden" rows, so in my example it doesnt count 2 but 7..
I've used .Range.SpecialCells(xlCellTypeVisible)before, but It doesn't seem to function with the cells.find above.
Does someone have an Idea on how I can count the visible (=filtered) Table, and then put it in a Listbox?
EDIT: I populate the listbox (unfiltered) like this:
Dim lastrow As Long
With Sheets("Data")
lastrow = .Cells(.Rows.Count, "R").End(xlUp).Row
End With
With Offene_PZ_Form.Offene_PZ
.ColumnCount = 18
.ColumnWidths = "0;80;0;100;100;0;50;50;80;50;0;0;0;0;0;150;150;0"
.List = Sheets("Data").Range("A2:R" & lastrow).Value
End With
But this won't work with filtered Data.
Here is a fun little fact, Excel creates an hidden named range once you start filtering data. If you have continuous data (headers/rows) this would return your range without looking for it. Though since it seem to resemble UsedRange it may still be better to search your last used column and row and create your own Range variable to filter. For this exercise I'll leave it be. Furthermore, as indicated in the comments above, one can loop over Areas of visible cells. I'd recommend a check beforehand just to be safe that there is filtered data other than headers.
Sub Test()
Dim ws As Worksheet: Set ws = ThisWorkbook.Worksheets("Data")
Dim Area as Range
ws.Cells(1, 1).AutoFilter 18, "WAHR"
With ws.Range("_FilterDatabase")
If .SpecialCells(12).Count > .Columns.Count Then
For Each Area In .Offset(1).Resize(.Rows.Count - 1, .Columns.Count).SpecialCells(12).Areas
Debug.Print Area.Address 'Do something
Next
End If
End With
End Sub
The above works if no headers are missing obviously.
Here is a VBA code to populate UserForm1.ListBox1.List with filtered rows.
Thanks to #FaneDuru for improvements in the code edited as per his comments.
In Userform1 code
Private Sub UserForm_Initialize()
PopulateListBoxWithVisibleCells
End Sub
In Module
Sub PopulateListBoxWithVisibleCells()
Dim wb As Workbook, ws As Worksheet
Dim filtRng As Range, rw As Range
Dim i As Long, j As Long, x As Long, y As Long, k As Long, filtRngArr
i = 0: j = 0: x = 0: y = 0
Set wb = ThisWorkbook: Set ws = wb.Sheets("Sheet1")
Set filtRng = ws.UsedRange.Cells.SpecialCells(xlCellTypeVisible)
For Each Area In filtRng.Areas
x = x + Area.Rows.Count
Next
y = filtRng.Columns.Count
ReDim filtRngArr(1 To x, 1 To y)
For k = 1 To filtRng.Areas.Count
For Each rw In filtRng.Areas(k).Rows
i = i + 1
arr = rw.Value
For j = 1 To y
filtRngArr(i, j) = Split(Join(Application.Index(arr, 1, 0), "|"), "|")(j - 1)
Next
Next
Next
With UserForm1.ListBox1
.ColumnCount = y
.List = filtRngArr
End With
End Sub
We can also add more fields say row number like Split(rw.Row & "|" & Join(Application.Index(arr, 1, 0), "|"), "|")(j - 1) but for every such intended column increments, we need to increment value of y like y = filtRng.Columns.Count + 1
In order to find x (Number of rows) we don't need the first loop... Simply, x = filtRng.Cells.Count / filtRng.Columns.Count is enough
Try, please the next code, if you want to use a continuous (built) array. It is possible to build it from the discontinuous range address, too:
Sub Filter_Offene()
Dim sh As Worksheet, lastRow As Long, rngFilt As Range, arrFin As Variant
Set sh = Sheets("Data")
lastRow = sh.Range("R" & Rows.count).End(xlUp).Row
rngFilt.AutoFilter field:=18, Criteria1:="WAHR"
Set rngFilt = rngFilt.Offset(1).SpecialCells(xlCellTypeVisible)
arrFin = ContinuousArray(rngFilt, sh, "R:R")
With ComboBox1
.list = arrFin
.ListIndex = 0
End With
End Sub
Private Function ContinuousArray(rngFilt As Range, sh As Worksheet, colLet As String) As Variant
Dim arrFilt As Variant, El As Variant, arFin As Variant
Dim rowsNo As Long, k As Long, i As Long, j As Long, arrInt As Variant
arrFilt = Split(rngFilt.address, ",")' Obtain an array of areas addresses
'real number of rows of the visible cells range:
For Each El In arrFilt
rowsNo = rowsNo + Range(El).Rows.count
Next
'redim the final array at the number of rows
ReDim arFin(1 To rowsNo, 1 To rngFilt.Columns.count)
rowsNo = 1
For Each El In arrFilt 'Iterate between the areas addresses
rowsNo = Range(El).Rows.count 'number of rows of the area
arrInt = ActiveSheet.Range(El).value' put the area range in an array
For i = 1 To UBound(arrInt, 1) 'fill the final array
k = k + 1
For j = 1 To rngFilt.Columns.count
arFin(k, j) = arrInt(i, j)
Next j
Next i
Next
ContinuousArray = arFin
End Function

Trying to copy data from one workbook to other using for loop, could anyone see whats wrong?

This code is supposed to transfer pivot table data from one workbook to other, works fine for a few loops, however, the row increment which is being tried isn't catching up after a few loops. I'm not sure why it messes up after running a few loops!
Sub copypivotdata()
Dim x As Workbook
Dim y As Workbook
Dim n As String, ws As Worksheet
Dim pvt As Range
Dim i, i2 As Integer
Set x = Workbooks("WorkbookA.xlsm")
Set y = Workbooks("WorkbookB.xlsm")
i = 2
'Now, transfer values from x to y:
x.Activate
For Each ws In ActiveWorkbook.Worksheets
'With ActiveWorkbook.ActiveSheet
If ws.Name <> "Main Sheet" And ws.ChartObjects().Count <> 0 Then
ws.Activate
Else:
GoTo nextws
End If
ws.Activate
Range("A1048576").Select
Selection.End(xlUp).Select
Range(Selection, Selection.End(xlUp)).Select
Range(Selection, Selection.End(xlToRight)).Select
Set pvt = Selection
i2 = Selection.Rows.Count
'MsgBox i2
y.Activate 'activate other workbook
'With
y.Sheets("Pivot data").Range("C" & i, "D" & i2 + i) = pvt.Value
i = i2 + 2
i2 = 0
nextws: Next ws
y.Activate
End Sub
The loop logic was incorrect.. Just realized.
"i = i2 + 2" is incorrect. Should be i = i + i2

Copying looped data from one workbook and paste to another

I have written this code, and it works when doing it from one sheet to another. (Same workbook). But when i loop through the rows from workbook to workbook i get "Run time error 9" Subscript out of range.
I've checked several times if the filenames are as stated in the code, and it doesn't seem to be the problem. Also if I in the first piece write y.sheets("Tavledisplay") instead of worksheets("Tavledisplay") the debugger tells me there's a problem there. Doing it the latter way, it sends 1 loop of data, and stops at y.sheets("Tavledisplay").Activate.
My code:
Dim x As Workbook
Dim y As Workbook
Set x = Workbooks.Open("C:\Users\u054939\Desktop\Diverse filer\Safecard\Safecardmaster.xlsm")
Set y = Workbooks.Open("C:\Users\u054939\Desktop\Diverse filer\Safecard\Tavleark1.xlsm")
a = Cells(Rows.Count, 1).End(xlUp).Row
For i = 2 To a
If Worksheets("Tavledisplay").Cells(i, 14).Value = "Ja" Then
Worksheets("Tavledisplay").Rows(i).Select
Selection.Copy
x.Sheets("Løsninger").Activate
b = Worksheets("Løsninger").Cells(Rows.Count, 1).End(xlUp).Row
x.Sheets("Løsninger").Cells(b + 1, 1).Select
ActiveSheet.Paste
y.Sheets("Tavledisplay").Activate
Selection.ClearContents
End If
Next i
Application.CutCopyMode = False
x.Sheets("Løsninger").Select
I expect the code to loop through all the given rows, where there is a "Ja" in column 14, and pasting them into my other workbook sheet "Løsninger" and deleting them from the other workbook.
You don't need to loop through each loop, a simple filter will do the trick:
Option Explicit
Sub Test()
Dim x As Workbook
Dim y As Workbook
Dim CopyRange As Range
Dim LastRow As Long
Set x = Workbooks.Open("C:\Users\u054939\Desktop\Diverse filer\Safecard\Safecardmaster.xlsm")
Set y = Workbooks.Open("C:\Users\u054939\Desktop\Diverse filer\Safecard\Tavleark1.xlsm")
'Look for the range to copy and set it
With y.Worksheets("Tabledisplay")
.UsedRange.AutoFilter Field:=14, Criteria1:="Ja"
LastRow = .Cells(.Rows.Count, 14).End(xlUp).Row
Set CopyRange = .Range("A2", .Cells(LastRow, .UsedRange.Columns.Count)).SpecialCells(xlCellTypeVisible)
.AutoFilterMode = False
End With
'Paste it to the other sheet
With x.Worksheets("Løsninger")
LastRow = .Cells(.Rows.Count, 1).End(xlUp).Row + 1
CopyRange.Copy .Cells(LastRow, 1)
End With
'Delete the range from the original sheet
CopyRange.Delete
End Sub

Exceeding row limit - create new sheet

I have 2 columns on a sheet "list", one column that lists all business entities, the other lists all org units. The functionality of the code below works perfectly but returns an error because it exceeds the sheet row limit.
The data is pasted onto a sheet "cc_act" is there a way to at point of error create a new sheet called "cc_act1"...."cc_act2" until the script is complete?
Declare Function HypMenuVRefresh Lib "HsAddin" () As Long
Sub cc()
Application.ScreenUpdating = False
Dim list As Worksheet: Set list = ThisWorkbook.Worksheets("list")
Dim p As Worksheet: Set p = ThisWorkbook.Worksheets("p")
Dim calc As Worksheet: Set calc = ThisWorkbook.Worksheets("calc")
Dim cc As Worksheet: Set cc = ThisWorkbook.Worksheets("cc_act")
Dim cc_lr As Long
Dim calc_lr As Long: calc_lr = calc.Cells(Rows.Count, "A").End(xlUp).Row
Dim calc_lc As Long: calc_lc = calc.Cells(1,
calc.Columns.Count).End(xlToLeft).Column
Dim calc_rg As Range
Dim ctry_rg As Range
Dim i As Integer
Dim x As Integer
list.Activate
For x = 2 To Range("B" & Rows.Count).End(xlUp).Row
If list.Range("B" & x).Value <> "" Then
p.Cells(17, 3) = list.Range("B" & x).Value
End If
For i = 2 To Range("A" & Rows.Count).End(xlUp).Row
If list.Range("A" & i).Value <> "" Then
p.Cells(17, 4) = list.Range("A" & i).Value
p.Calculate
End If
p.Activate
Call HypMenuVRefresh
p.Calculate
'''changes country on calc table
calc.Cells(2, 2) = p.Cells(17, 4)
calc.Cells(2, 3) = p.Cells(17, 3)
calc.Calculate
'''copy the calc range and past under last column
With calc
Set calc_rg = calc.Range("A2:F2" & calc_lr)
End With
With cc
cc_lr = cc.Cells(Rows.Count, "A").End(xlUp).Row + 1
calc_rg.Copy
cc.Cells(cc_lr, "A").PasteSpecial xlPasteValues
End With
Next i
Next x
Application.ScreenUpdating = True
End Sub
I suppose there are a few ways to handle something like this. See the code sample below, and adapt it to your specific needs.
Sub LongColumnToAFewColumns()
Dim wsF As Worksheet, WST As Worksheet
Dim rf As Range, rT As Range
Dim R As Long, j As Integer
' initialize
Set wsF = ActiveSheet
Set WST = Sheets.Add
WST.Name = "Results"
j = 1
For R = 1 To wsF.Cells(Rows.Count, 1).End(xlUp).Row Step 65536
wsF.Cells(R, 1).Resize(65536).Copy
WST.Cells(j, 1).PasteSpecial xlPasteValues
WST.Cells(j, 1).PasteSpecial xlPasteValues
j = j + 1
Next R
End Sub
As an aside, you may want to consider using MS Access for this kind of thing. Or, better yet, Python or even R. Good luck with your project.

Resources