Add Rows below row 1, Fill first cell with docvariable and increment - vba

Solved my own problem. I needed to first check if column 2 cells were empty and if so delete said row. This caused issues due to last row of table being merged across. I then needed to add rows below first row, to maintain 4 columns in each row, in table based on user selection of ArraySize in userform. Then populate first cell in each row with a docvariable in userform followed by incrementing number in each row. Then sort table in descending order. Here is my code for future use.
Private Sub cbArraySize_Click()
If cbArraySize.Value <> 0 Then
DeleteRows
AddRows
AddArrayName
TableSort
End If
End Sub
Sub DeleteRows()
Dim tbl As Word.Table
Dim nrRows As Long, ColToCheck As Long, i As Long
Dim cellRange As Word.Range
Set tbl = ActiveDocument.Tables(2)
nrRows = tbl.Rows.Count - 1
ColToCheck = 2
For i = nrRows To 1 Step -1
Set cellRange = tbl.Cell(i, ColToCheck).Range
If Len(cellRange.Text) = 2 Then
cellRange.Rows(1).Delete
End If
Next i
End Sub
Sub AddRows()
With ActiveDocument
.Tables(2).Rows(1).Select
Selection.InsertRowsBelow (cbArraySize.Value)
End With
End Sub
Sub AddArrayName()
With ActiveDocument
Dim tbl As Object
Dim noOfCol As Integer
Dim i As Long
Dim intcount As Integer
Set tbl = .Tables(2)
With tbl
noOfCol = tbl.Range.Rows(1).Cells.Count
For i = .Rows.Count To 1 Step -1
With .Rows(i)
If Len(.Range) = noOfCol * 2 + 2 Then .Cells(1).Range.InsertAfter Text:=tbArrayName.Text + " - " & intcount
intcount = intcount + 1
End With
Next i
End With
End With
End Sub
Sub TableSort()
ActiveDocument.Tables(2).Sort ExcludeHeader:=True
End Sub

Posted my working code in original post.

Related

Delete entire Row with zeroes apply to all worksheets

I have VBA code that works to delete entire row when there is an absolute zero value in one a cell column but, I am not able to figure out how to update code to apply to all worksheets (there are 20 Sheets in my workbook):
Can someone help with syntax how to update this code to apply to all worksheets in the workbook.
Sub IfandthenDelete_Button3_Click()
Dim lRow As Long
Dim i As Long
lRow = 3000
Application.ScreenUpdating = False
For i = lRow To 1 Step -1
If Cells(i, 1) = 0 Then
Rows(i).Delete
End If
Next
Application.ScreenUpdating = False
End Sub
You need one more for loop for that.
Sub WorksheetLoop()
Dim wsCount As Integer
Dim j As Integer
Dim lRow As Long
Dim i As Long
lRow = 3000
wsCount = ActiveWorkbook.Worksheets.Count
For j = 1 To wsCount
For i = lRow To 1 Step -1
If ActiveWorkbook.Worksheets(j).Cells(i, 1) = 0 Then
ActiveWorkbook.Worksheets(j).Rows(i).Delete
End If
Next
Next j
End Sub

Excel crashes without errors or indication why when running simple VBA code?

Excel has started crashing without any explanation and the code has worked previously so I am not exactly sure why it is constantly crashing now. It isn't even that much code or very complex. I am simply adding new values to a table in two separate worksheets. Any help would be greatly appreciated!
Sheet 1:
Option Explicit
Private Sub CommandButton1_Click()
Dim nameStr As String
nameStr = TextBox1.Value
Dim costInt As Integer
costInt = TextBox2.Value
Dim newRateValues(8)
newRateValues(0) = nameStr
newRateValues(1) = costInt
newRateValues(2) = costInt
newRateValues(3) = costInt
newRateValues(4) = costInt
newRateValues(5) = costInt
newRateValues(6) = costInt
newRateValues(7) = costInt
newRateValues(8) = costInt
AddDataRow "ratesTable", newRateValues
AddDataRow2 "tableResources"
End Sub
Module:
Option Explicit
Sub AddDataRow(tableName As String, values() As Variant)
Dim sheet As Worksheet
Dim table As ListObject
Dim col As Integer
Dim lastRow As Range
Set sheet = ActiveWorkbook.Worksheets("Sheet1")
Set table = sheet.ListObjects.Item(tableName)
'First check if the last row is empty; if not, add a row
If table.ListRows.count > 0 Then
Set lastRow = table.ListRows(table.ListRows.count).Range
For col = 1 To lastRow.Columns.count
If Trim(CStr(lastRow.Cells(1, col).Value)) <> "" Then
table.ListRows.Add
Exit For
End If
Next col
Else
table.ListRows.Add
End If
'Iterate through the last row and populate it with the entries from values()
Set lastRow = table.ListRows(table.ListRows.count).Range
For col = 1 To lastRow.Columns.count
If col <= UBound(values) + 1 Then lastRow.Cells(1, col) = values(col - 1)
Next col
End Sub
Sub AddDataRow2(tableName As String)
Dim sheet As Worksheet
Dim table As ListObject
Dim col As Integer
Dim lastRow As Range
Dim newRate As Integer
Set sheet = ActiveWorkbook.Worksheets("Sheet2")
Set table = sheet.ListObjects.Item(tableName)
newRate = ActiveSheet.TextBox1.Text
'First check if the last row is empty; if not, add a row
If table.ListRows.count > 0 Then
Set lastRow = table.ListRows(table.ListRows.count).Range
For col = 1 To lastRow.Columns.count
If Trim(CStr(lastRow.Cells(1, col).Value)) <> "" Then
table.ListRows.Add
Exit For
End If
Next col
Else
table.ListRows.Add
End If
'Iterate through the last row and populate it with the entries from values()
Set lastRow = table.ListRows(table.ListRows.count).Range
lastRow.Cells(1, 2) = newRate
End Sub

VBA Removing ListBox Duplicates

I'm trying to add a list of names from another worksheet that has duplicates. On the listbox, I want to have unique names, instead of duplicates. The following code is not sorting them for duplicates, it errors out. Any help is appreciated.
Dim intCount As Integer
Dim rngData As Range
Dim strID As String
Dim rngCell As Range
dim ctrlListNames as MSForms.ListBox
Set rngData = Application.ThisWorkbook.Worksheets("Names").Range("A").CurrentRegion
'declare header of strID and sort it
strID = "Salesperson"
rngData.Sort key1:=strID, Header:=xlYes
'Loop to add the salesperson name and to make sure no duplicates are added
For Each rngCell In rngData.Columns(2).Cells
If rngCell.Value <> strID Then
ctrlListNames.AddItem rngCell.Value
strID = rngCell.Value
End If
Next rngCell
Way 1
Use this to remove the duplicates
Sub Sample()
RemovelstDuplicates ctrlListNames
End Sub
Public Sub RemovelstDuplicates(lst As msforms.ListBox)
Dim i As Long, j As Long
With lst
For i = 0 To .ListCount - 1
For j = .ListCount - 1 To (i + 1) Step -1
If .List(j) = .List(i) Then
.RemoveItem j
End If
Next
Next
End With
End Sub
Way 2
Create a unique collection and then add it to the listbox
Dim Col As New Collection, itm As Variant
For Each rngCell In rngData.Columns(2).Cells
On Error Resume Next
Col.Add rngCell.Value, CStr(rngCell.Value)
On Error GoTo 0
Next rngCell
For Each itm In Col
ctrlListNames.AddItem itm
Next itm
Private Sub Workbook_Open()
Dim ctrlListNames As MSForms.ListBox
Dim i As Long
Dim j As Long
ctrlListNames.List = Application.ThisWorkbook.Worksheets("Names").Range("Salesperson").Value
With ctrlListNames
For i = 0 To .ListCount - 1
For j = .ListCount To (i + 1) Step -1
If .List(j) = .List(i) Then
.RemoveItem j
End If
Next
Next
End With
End Sub
And it says invalid property array index.
It says invalid property array index because the list gets shortened after the removal of entries. if we use FOR, the end value is static, therefore, we need to use DO while loop. Use the following code to remove duplicates.
Count = ListBox1.ListCount - 1
i = 0
j = 0
Do While i <= Count
j = i + 1
Do While j <= Count
If ListBox1.List(i) = ListBox1.List(j) Then
ListBox1.RemoveItem (j)
Count = ListBox1.ListCount - 1 'Need to update list count after each removal.
End If
j = j + 1
Loop
i = i + 1
Loop

Randomise numbers without repeating the number

My end result is to output the names in column A to column B in random order.
I have been researching but cant seem to find what I need.
So far I can kinda of randomise the numbers but its still giving me repeated numbers + the heading (A1).
I need it to skip A1 because this is the heading\title of the column and start at A2.
I assume once that is working correctly I add the randomNumber to a random name to Worksheets("Master Sheet").Cells(randomNumber, "B").Value ...something like that...?
OR if there is a better way of doing this let me know.
Sub Meow()
Dim CountedRows As Integer
Dim x As Integer
Dim i As Integer
Dim PreviousCell As Integer
Dim randomNumber As Integer
i = 1
PreviousCell = 0
CountedRows = Worksheets("Master Sheet").Range("A" & Rows.Count).End(xlUp).Row
If CountedRows < 2 Then
' If its only the heading then quit and display a messagebox
No_People_Error = MsgBox("No People entered or found, in column 'A' of Sheetname 'Master Sheet'", vbInformation, "Pavle Says No!")
Exit Sub
End If
Do Until i = CountedRows
randomNumber = Int((Rnd * (CountedRows - 1)) + 1) + 1
If Not PreviousCell = randomNumber Then
Debug.Print randomNumber
i = i + 1
End If
PreviousCell = randomNumber
Loop
Debug.Print "EOF"
End Sub
Here's a quick hack...
Sub Meow()
'On Error GoTo err_error
Dim CountedRows As Integer
Dim x As Integer
Dim i As Integer
Dim PreviousCell As Integer
Dim randomNumber As Integer
Dim nums() As Integer
PreviousCell = 0
CountedRows = Worksheets("Master Sheet").Range("A" & Rows.Count).End(xlUp).Row
ReDim nums(CountedRows - 1)
If CountedRows < 2 Then
' If its only the heading then quit and display a messagebox
No_People_Error = MsgBox("No People entered or found, in column 'A' of Sheetname 'Master Sheet'", vbInformation, "Pavle Says No!")
Exit Sub
End If
For i = 1 To CountedRows
rand:
randomNumber = randomNumbers(1, CountedRows, nums)
nums(i - 1) = randomNumber
Worksheets("Master Sheet").Range("B" & randomNumber) = Range("A" & i)
Next i
Exit Sub
err_error:
Debug.Print Err.Description
End Sub
Public Function randomNumbers(lb As Integer, ub As Integer, used As Variant) As Integer
Dim r As Integer
r = Int((ub - lb + 1) * Rnd + 1)
For Each j In used
If j = r Then
r = randomNumbers(lb, ub, used)
Else
randomNumbers = r
End If
Next
End Function
I've managed something similar previously using two collections.
Fill one collection with the original data and leave the other collection empty. Then keep randomly picking an index in the first collection, adding the value at that index to the second collection and delete the value from the original collection. Set that to loop until the first collection is empty and the second collection will be full of a randomly sorted set of unique values from your starting list.
***Edit: I've thought about it again and you don't really need the second collection. You can pop a random value from the first collection and write it directly to the worksheet, incrementing the row each time:
Sub Meow()
Dim lst As New Collection
Dim rndLst As New Collection
Dim startRow As Integer
Dim endRow As Integer
Dim No_People_Error As Integer
startRow = 2
endRow = Worksheets("Master Sheet").Cells(startRow, 1).End(xlDown).Row
If Cells(startRow, 1).Value = "" Then
' If its only the heading then quit and display a messagebox
No_People_Error = MsgBox("No People entered or found, in column 'A' of Sheetname 'Master Sheet'", vbInformation, "Pavle Says No!")
Exit Sub
End If
' Fill a collection with the original list
Dim i As Integer
For i = startRow To endRow
lst.Add Cells(i, 1).Value
Next i
' Create a randomized list collection
' Use i as a row counter
Dim rowCounter As Integer
rowCounter = startRow
Dim index As Integer
Do While lst.Count > 0
'Find a random index in the original collection
index = Int((lst.Count - 1 + 1) * Rnd + 1)
'Place the value in the worksheet
Cells(rowCounter, 2).Value = lst(index)
'Remove the value from the list
lst.Remove (index)
'Increment row counter
rowCounter = rowCounter + 1
Loop
End Sub
P.S. I hope there's an excellent story behind naming your sub Meow() :P

VBA script should return name of worksheet if a non zero value exists in a certain range

I'm running a report that will churn out a single workbook with multiple worksheets. However, I'm only interested in finding out if there is a non zero value in a range; i.e Range("B33:J33"). Assuming out of the 10 worksheets, 3 of them have non zero values in the suggested range.
Would I be able to prompt a message box that tells me which 3 worksheets have the non zero values?
Dim rngToCheck As Range
Set rngToCheck = Range("B33:J33")
Dim cell As Range
Dim counter As Integer
Dim active As Integer, count As Integer
Worksheets(1).Select
count = Worksheets.count
active = ActiveSheet.Index + 1
counter = 0
Do Until active > count
rngToCheck.Select
For Each cell In Selection
If cell.Value <> 0 Then
counter = counter + 1
End If
ActiveSheet.Next.Select
Next
Loop
msgbox counter
First define at your sub:
Dim rngToCheck as Range
Set rngToCheck = Range("B33:J33")
Dim cell as Range
Dim counter as Integer
counter =0
Run a FOR EACH loop for every sheet and sheet, and inside it run another FOR EACH loop that says:
rngToCheck.Select
FOR EACH cell in Selection
IF cell.Value <>"0" then
counter = counter+1
end if
next
and then, just a simple if condition for the counter value, if it will be zero, no cell found, else did found...
You can try this
Dim wr, r As Integer, nr As Integer
nr = Sheets.Count
For r = 1 To nr
With Sheets(r)
If Application.CountIf(.Range("B33:J33"), "<>0") - Application.CountBlank(.Range("B33:J33")) > 0 Then
MsgBox .name
End If
End With
Next
I assume you don't want, as well, empty cells.
Dim sheetNames(1 to 100) As String
Dim snIndex As Integer
snIndex = 1
For Each sheet In ActiveWorkbook.Worksheets
For Each cll in rngToCheck
If cll.Value <> "" Then
If IsNumeric(cll.Value) Then
If cll.Value > 0 Then
sheetNames(snIndex) = sheet.Name
snIndex = snIndex + 1
Break For
End If
End If
End If
Next cll
Next sht
Dim message As String
message = ""
Dim i As Integer
For i = 1 to snIndex - 1
message = message & "; " & sheetNames(i)
Next i
msgbox message