Copy a range of cells down a specified number of times based on a cell value - excel

I'm trying to copy a range of cells down a specified number of times based on a cell value, and starting at the intersection of the active cell.
I've pored through pages of Stack Overflow issues.
Entire Sub with userform info.
Private Sub OKButton_Click()
Dim AppTab As String
Dim DDate As Date
Dim Rent As Long
Dim ActiveCost As Long
Dim Msg As String
AppTab = Application.Value
DDate = DispoDate.Value
Rent = RentPymt.Value
ActiveCost = Cost.Value
Msg = "Asset disposal date:"
Sheets(AppTab).Select
Range("A6:N11").Select
Selection.copy
Range("A9").Select
Selection.End(xlToRight).Offset(-3, 1).Select
ActiveSheet.Paste
ActiveCell.Offset(-5, 0).Select
ActiveCell.Value = Msg
ActiveCell.Offset(1, 0).Select
ActiveCell.Value = DDate
ActiveCell.Offset(8, 5).Select
ActiveCell.Value = ActiveCost
ActiveCell.Offset(1, -5).Activate
Dim DataEntry As Worksheet, DataSht As Worksheet
Dim ItemName As Range, ItemCount As Range
Dim NRow As Long, TargetCell As Range
With ThisWorkbook
Set DataEntry = .ActiveSheet
Set DataSht = .ActiveSheet
End With
With DataEntry
Set ItemName = .Range("A11")
Set ItemCount = .Range("H3")
End With
NCol = ActiveCell.Column
With DataSht
NRow = .Range("A" & Rows.Count).End(xlUp).Row + 1
'Set TargetCell = .Range("A" & NRow) 'This works
Set TargetCell = .Cells(NRow, NCol) 'Issue here
TargetCell.Resize(ItemCount.Value, 1).Value = ItemName.Value
End With
Range(Selection, Selection.End(xlToRight)).Select
Selection.copy
Range(Selection, Selection.End(xlDown)).Select
ActiveSheet.Paste
Unload Me
End Sub
Getting
run-time 1004: Method 'Range' of object '_Worksheet'failed
I'm building amortization schedules for a portfolio of assets. When one disposes, I need to modify the amortization schedule for the new asset cost/rental payment, and track it at two different rates. Initiated by a userform where they enter the updated asset info.
I can run the original amortization schedule code, but I need the subsequent partial disposals to be dynamic as the portfolio could have hundreds of assets. (Let's not talk about how inefficient that is because the customer is always right and currently I'm doing it by copying and pasting.)

I've made some assumptions based on what you described so far, and what your code was doing already. Please let me know if it works as you need, or let me know and I can assist further.
See more comments in the code:
Private Sub OKButton_Click()
Dim AppTab As String
Dim DDate As String
Dim Rent As String 'this is never used
Dim ActiveCost As String
Dim Msg As String
AppTab = Application.Value 'This doesn't look quite right, it would return "Microsoft Excel" ... is that your sheet name?
DDate = DispoDate.Value
Rent = RentPymt.Value 'this is never used
ActiveCost = Cost.Value
Msg = "Asset disposal date:"
Dim DataEntry As Worksheet: Set DataEntry = ThisWorkbook.Sheets(AppTab) 'declare and set the worksheet to use - change as needed
Dim rngCopy As Range: Set rngCopy = DataEntry.Range("A6:N11") 'Set the range to copy - this could be determined more dynamically
Dim ItemCount As Long: ItemCount = DataEntry.Range("H3").Value 'set the number of rows to copy
With rngCopy
.Copy _
Destination:=.Offset(, .Columns.Count) 'Copy ("A6:N11") to ("O6:AB11")
.Offset(.Rows.Count - 1).Resize(1, .Columns.Count).Copy _
Destination:=.Offset(.Rows.Count, .Columns.Count).Resize(ItemCount, .Columns.Count) 'Copy the last line from above, to the number of the rows in ItemCount
End With
'Is not a good idea to use ActiveCell... better use a fixed range, or build some rules to determine your "active" cell (i.e.: use Find).
Dim rngActCell As Range: Set rngActCell = DataEntry.Range("P6") 'but if you insist in using ActiveCell, then use: Set rngActCell = Activecell
'Other details
With rngActCell
.Offset(-5, 0).Value = Msg 'P1
.Offset(-4, 0).Value = DDate 'P2
.Offset(4, 5).Value = ActiveCost 'U10
End With
Unload Me
End Sub

Related

Copy range of data from sheet 1 Inputs to a sheet 2 inspection log

I have created a spreadsheet with a sheet 1 input table, and want to transfer/copy that data into a sheet 2 log table. The input table on sheet 1 will have an inspection date and an inspection name cells. What I am having an issue with is that I can get the first line of the log to input, but the 2nd line I get a "Run0time error '1004': Application-defined or object defined error". Not sure what to look at from here.
Here's my code (I know, it's stiff rough and needs to be cleaned up):
Private Sub Add_Click()
Dim InspectionDate As String, InspectionName As String
Dim LastRow As Long
Worksheets("sheet1").Select
InspectionDate = Range("B4")
InspectionName = Range("B5")
Worksheets("sheet2").Select
Worksheets("sheet2").Range("B3").Select
If Worksheets("sheet2").Range("B3").Offset(1, 0) <> "" Then
Worksheets("sheet2").Range("B3").End(x1Down).Select
End If
ActiveCell.Offset(1, 0).Select
ActiveCell.Value = InspectionDate
ActiveCell.Offset(0, 1).Select
ActiveCell.Value = InspectionName
Worksheets("sheet1").Select
Worksheets("sheet1").Range("B4:B5").ClearContents
End Sub
Two main reasons why .Select, .Activate, Selection, Activecell, Activesheet, Activeworkbook, etc. should be avoided
The reasons are explained in the second answer on that page.
I have tested the code below and it works for me.
I'm autistic; so sometimes I appear to school others, when I'm only trying to help.
Option Explicit
Private Sub Add_Click()
Dim InspectionDate$, InspectionName$
Dim LastRow&
Dim WS As Worksheet, WS2 As Worksheet
Set WS = Worksheets("sheet1")
Set WS2 = Worksheets("sheet2")
InspectionDate = WS.Range("B4")
InspectionName = WS.Range("B5")
LastRow = 3
If WS2.Range("B" & LastRow + 1) <> "" Then
LastRow = WS2.Range("B" & Rows.count - 1).End(xlUp).Row
End If
WS2.Cells(LastRow + 1, 2) = InspectionDate
WS2.Cells(LastRow + 1, 3) = InspectionName
WS.Range("B4:B5").ClearContents
End Sub

How to move a data set from one sheet to another based on criteria using VBA

I am trying to move data from one Table called Raw_Data on sheet Raw Data to another table called Phone_Number on sheet No Quality.
I have 16 columns on the tables and I need to confirm if the Raw Data table has the words No Quality or PH Phone on the 15th column. If it does then I want to move the data to the No Quality sheet and paste it into the table there. Once it is pasted I want to erase the data off of the Raw Data table.
I have tried a few different methods but can't seem to get them to work. Here is the first method I'm using
Sub Numbers()
Dim dataSheet As Worksheet, newSheet As Worksheet
Dim dataTable As ListObject, newTable As ListObject
Dim dataCount As Long
Dim checkOne As String, checkTwo As String
Dim copyRange As Range
Set dataSheet = Worksheets("Raw Data")
Set newSheet = Worksheets("No Quality")
Set dataTable = dataSheet.ListObjects("Raw_Data")
Set newTable = newSheet.ListObjects("Phone_Number")
checkOne = "PH Phone"
checkTwo = "No Quality"
dataCount = dataSheet.ListObjects("Raw_Data").ListRows.Count
dataValue = dataSheet.ListObjects("Raw_Data").DataBodyRange(dataCount, "O").Value
dataLocation = dataSheet.ListObjects("Raw_Data").DataBodyRange(dataCount, "O").row - 1
For i = 2 To dataLocation
valueToCheck = dataSheet.ListObjects("Raw_Data").DataBodyRange(i, "O")
If valueToCheck = checkOne Or valueToCheck = checkTwo Then
'Errors out on the line below
Worksheets("Raw Data").Range(Cells(i, "A"), Cells(i, "P")).Copy
Worksheets("No Quality").Cells(Cells(Rows.Count, 1).End(xlUp).Offset(1, 0).row, 1).PasteSpecial
End If
Next i
End Sub
I can get it to partially run but it will never complete. I attempted to use the following code but I'm not sure how to change it to run in the way that I needed it.
Sub NoQuality()
Dim dataTable As Range
Dim newTable As Range
Application.ScreenUpdating = False
Set dataTable = Worksheets("Raw Data").ListObjects("Raw_Data").DataBodyRange
Set newTable = Worksheets("No Quality").ListObjects("Phone_Number").DataBodyRange
dataTable.Copy newTable.Offset(tbl2.Rows.Count)
Application.CutCopyMode = False
tbl1.ClearContents
Application.ScreenUpdating = True
End Sub
Results of New Code
You could probably get away with a lot less code. Please try the following & let me know how it goes.
Option Explicit
Sub Numbers()
Dim ws1 As Worksheet, ws2 As Worksheet
Set ws1 = Sheets("Raw Data")
Set ws2 = Sheets("No Quality")
With ws1.ListObjects("Raw_Data").Range
.AutoFilter 15, "No Quality", 2, "PH Phone"
.Offset(1).Resize(.Rows.Count - 1).Copy ws2.Cells(2, 1)
.Offset(1).Resize(.Rows.Count - 1).EntireRow.Delete
ws1.ListObjects("Raw_Data").AutoFilter.ShowAllData
End With
End Sub

Search column headers and insert new column using Excel VBA

I have a spreadsheet that is updated regularly. Therefore the column header positions change regularly. eg. today "Username" is column K, but tomorrow "Username" might be column L. I need to add a new column to the right of "Username" but where it changes I cannot refer to as cell/column reference.
So far I have:
Dim rngUsernameHeader As Range
Dim rngHeaders As Range
Set rngHeaders = Range("1:1") 'Looks in entire first row.
Set rngUsernameHeader = rngHeaders.Find("Username")
When I go to add a new column to the right of it, I'm selecting that row but it's going back to cell/column references...
Columns("K:K").Select
Selection.Insert Shift:=xlToRight
Range("K1").Select
ActiveCell.FormulaR1C1 = "Role"
How can I perform this step with a macro?
edit: I think need to give that Column a header name and begin populating the row with data - each time I do begins the cell references which I want to avoid wherever possible.
Many thanks in advance.
How about:
Sub qwerty()
Dim rngUsernameHeader As Range
Dim rngHeaders As Range
Set rngHeaders = Range("1:1") 'Looks in entire first row.
Set rngUsernameHeader = rngHeaders.Find(what:="Username", After:=Cells(1, 1))
rngUsernameHeader.Offset(0, 1).EntireColumn.Insert
rngUsernameHeader.Offset(0, 1).Value = "role"
End Sub
Sub AddColumn
Dim cl as Range
For each cl in Range("1:1")
If cl = "username" Then
cl.EntireColumn.Insert Shift:= xlToRight
End If
cl.Offset(0, 1) = "role"
Next cl
End Sub
Untested code as not at my desktop
Something like this should work. The idea is that you locate the column and then you insert to the right. That is why you have the +1 in the TestMe. The function l_locate_value_col returns the column, where it has found the value. If you want, you may change the optional parameter l_row, depending on which row do you want to look for.
Option Explicit
Public Sub TestMe()
Dim lngColumn As Long
lngColumn = l_locate_value_col("Username", ActiveSheet)
Cells(1, lngColumn + 1).EntireColumn.Insert
End Sub
Public Function l_locate_value_col(target As String, _
ByRef target_sheet As Worksheet, _
Optional l_row As Long = 1)
Dim cell_to_find As Range
Dim r_local_range As Range
Dim my_cell As Range
Set r_local_range = target_sheet.Range(target_sheet.Cells(l_row, 1), target_sheet.Cells(l_row, Columns.Count))
For Each my_cell In r_local_range
If target = Trim(my_cell) Then
l_locate_value_col = my_cell.Column
Exit Function
End If
Next my_cell
l_locate_value_col = -1
End Function
You could name your range:
Sub Test()
Dim rngUsernameHeader As Range
'UserName is in column F at the moment.
Set rngUsernameHeader = Range("UserName")
Debug.Print rngUsernameHeader.Address 'Returns $F$1
ThisWorkbook.Worksheets("Sheet2").Range("E:E").Insert Shift:=xlToRight
Debug.Print rngUsernameHeader.Address 'Returns $G$1
End Sub
Edit:
Have rewritten so it inserts a column after your named column and returns that reference:
Sub Test()
Dim rngUsernameHeader As Range
Dim rngMyNewColumn As Range
Set rngUsernameHeader = Range("UserName")
rngUsernameHeader.Offset(, 1).Insert Shift:=xlToRight
'You'll need to check the named range doesn't exist first.
ThisWorkbook.Names.Add Name:="MyNewRange", _
RefersTo:="='" & rngUsernameHeader.Parent.Name & "'!" & _
rngUsernameHeader.Offset(, 1).Address
Set rngMyNewColumn = Range("MyNewRange")
MsgBox rngMyNewColumn.Address
End Sub

Dynamic mnacro comparing two tables and adding row if not found on one table or updating info if row found but some info different

I am stuck writing this Excel macro and could kindly use some help. I am trying to create a dynamic macro that will compare two tables in two different sheets and will update information for a row if different or copy a new row to the new table if not there. Both tables contain the same columns of info and have a unique product code per data row. Once a button is pressed, if the product code for the row in table1 is not found on the new table then that row will copy. If the product code is found in the new table but other information in columns is different, than that other information will be updated on the new table. If the product code is found and the other information is the same then that row will not be copied. I need this for as many lines as possible in table1.
NOTE: I thought VLOOKUP may be the route to successfully code this macro...BELOW is my attempt so far to get this to work.
Sub Copy_Attempt()
Application.ScreenUpdating = False
Dim s1 As Worksheet, s2 As Worksheet
Set s1 = Sheets("Raw Data")
Set s2 = Sheets("BAS Linkage Master")
Dim i As Integer
Dim j As Integer
Dim Proj_ID As String
Dim Lookup_Range As Range
Dim Linkage_Lookup_Range As Range
Dim Raw_Percent_Complete As String
Dim Linkage_Percent_Complete As String
Set Lookup_Range = s1.Range("A1:O1000")
Set Linkage_Lookup_Range = s2.Range("A6:N1000")
For i = 2 To 1000
Proj_ID = s1.Range("F" & i).Value
Raw_Percent_Complete = Application.WorksheetFunction.VLookup(Proj_ID, Lookup_Range, 10, False)
Next
For j = 7 To 1000
Linkage_Percent_Complete = s2.Range("I" & j).Value
Next
If Raw_Percent_Complete = Linkage_Percent_Complete Then
' DO NOT COPY THAT ROW OVER
Else
Percent_Complete = Range("I" & j).Value
'UPDATE PERCENT COMPLETE FOR THAT SPECIFIC PRODUCT CODE
End If
Sheets("Raw Data").Activate
Columns("H").EntireColumn.Delete
Range("A2:P1000").Select
Selection.Copy
Sheets("BAS Linkage Master").Activate
Range("A" & Rows.Count).End(xlUp).Offset(1).Select
Selection.PasteSpecial
' Sheets("Welcome").Activate
' Range("A11:O11").ClearContents
Sheets("Raw Data").Activate
Range("A2:N10000").ClearContents
Application.CutCopyMode = False
Application.ScreenUpdating = True
Sheets("BAS Linkage Master").Activate
End Sub
This is a nice little script that looks for differences and highlights the differences.
Public Sub CompareSheets()
Dim ws1 As Worksheet, ws2 As Worksheet
Dim cell As Range, rng As Range
Set ws1 = Worksheets("Sheet1")
Set ws2 = Worksheets("Sheet2")
Set rng = ws1.Range("A1:B20")
For Each cell In rng
Celladdress = cell.Address
If cell <> ws2.Range(Celladdress) Then
cell.Interior.Color = vbYellow
ws2.Range(Celladdress).Interior.Color = vbYellow
End If
Next cell
End Sub
You can use the same concept to copy the values from one table to another.
Public Sub CompareSheets()
Dim ws1 As Worksheet, ws2 As Worksheet
Dim cell As Range, rng As Range
Set ws1 = Worksheets("Sheet1")
Set ws2 = Worksheets("Sheet2")
Set rng = ws1.Range("A1:B20")
For Each cell In rng
Celladdress = cell.Address
If cell <> ws2.Range(Celladdress) Then
ws2.Range(Celladdress).Value = ws1.Range(Celladdress).Value
End If
Next cell
End Sub

Copy rows in Excel if cell contains name from an array

I have an Excel sheet that contains entries for ~150 employees. Each row contains the name as well as hours worked, pay, team, etc etc etc etc. The B column in each row contains the employees name in Last,First format. About half the employees on the sheet are part time employees. What i'm trying to do is write a macro in VB that copies the entire row if the name in the B column matches one of the names of the part time employees so that one of my coworkers can simply run the macro and paste all of the rows of copied users into a new sheet each week. Here's what I currently have. (I have all of the employees names in the array however I have censored them out) I really don't understand much of the last 50% of the code. This stuff was stuff I found online and have been messing around with.
`Sub PartTime()
Dim strArray As Variant
Dim wsSource As Worksheet
Dim wsDest As Worksheet
Dim NoRows As Long
Dim DestNoRows As Long
Dim I As Long
Dim J As Integer
Dim rngCells As Range
Dim rngFind As Range
Dim Found As Boolean
nameArray = Array(NAMES CENSORED)
Set wsSource = ActiveSheet
NoRows = wsSource.Range("A65536").End(xlUp).Row
DestNoRows = 1
Set wsDest = ActiveWorkbook.Worksheets.Add
For I = 1 To NoRows
Set rngCells = wsSource.Range("C" & I & ":F" & I)
Found = False
For J = 0 To UBound(strArray)
Found = Found Or Not (rngCells.Find(strArray(J)) Is Nothing)
Next J
If Found Then
rngCells.EntireRow.Copy wsDest.Range("A" & DestNoRows)
DestNoRows = DestNoRows + 1
End If
Next I
End Sub`
This code should work for what you are looking for. It is important to note that the string names in your array must be identical to that in Column B (with the exception of leading and trailing spaces), so if the names are written "LastName, FirstName" then your input data must be identical. This code could be tweaked to not have this requirement, but for now I've left it as such. Let me know if you'd prefer the code be adjusted.
Option Explicit
Sub PartTimeEmployees()
Dim NewSheet As Worksheet, CurrentSheet As Worksheet, NameArray As Variant
Set CurrentSheet = ActiveWorkbook.ActiveSheet
Set NewSheet = Sheets.Add(After:=Sheets(Worksheets.Count))
NewSheet.Name = "Part Time Employees"
NameArray = Array("NAMES CENSORED")
'Pulling headers from the first row
CurrentSheet.Rows(1).EntireRow.Copy
NewSheet.Select 'Redundant but helps avoid the occasional error
NewSheet.Cells(1, 1).Select
ActiveSheet.Paste
CurrentSheet.Select
Dim NextRow As Long
NextRow = 2
'Writing this code to not assume that the data is continuous
Dim Count As Long
'Iterating to the end of the data in the sheet
For Count = 2 To CurrentSheet.UsedRange.Rows.Count
If Not IsEmpty(CurrentSheet.Cells(Count, 2)) Then
For Counter = 1 To UBound(NameArray)
'Performing string operations on the text will be faster than the find method
'It is also essential that the names are entered identically in your array
If UCase(Trim(CurrentSheet.Cells(Count, 2).Value)) = UCase(NameArray(Counter)) Then
CurrentSheet.Rows(Count).Copy
NewSheet.Select
NewSheet.Cells(NextRow, 1).Select
ActiveSheet.Paste
CurrentSheet.Select
NextRow = NextRow + 1
Exit For
End If
Next Counter
End If
Next Count
End Sub
No need to loop through the array if you use a Range.AutoFilter Method with the array as criteria.
See comment for each line of operational code.
Option Explicit
Sub partTimers()
Dim nameArray As Variant
'construct an array of the part-time employees' names
nameArray = Array("Trgh, Evtfk", "Mtre, Sdnrm", _
"Sfgd, Pxduj", "Lsds, Qwrml", _
"Eqrd, Oqtts")
With Worksheets("Sheet1") 'you should know what worksheet the names are on
'turn off AutoFilter is there is one already in operation
If .AutoFilterMode Then .AutoFilterMode = False
'use the 'island' of cells radiating out from A1
With .Cells(1, 1).CurrentRegion
'apply AutoFilter using array of names as criteria
.AutoFilter field:=2, Criteria1:=nameArray, Operator:=xlFilterValues
'check if there is anything to copy
If Application.Subtotal(103, .Columns(2)) > 1 Then
'copy the filtered range
.Cells.Copy
'create a new worksheet
With .Parent.Parent.Worksheets.Add(After:=Sheets(Sheets.Count))
'paste the filtered range, column widths and cell formats
.Cells(1, 1).PasteSpecial Paste:=xlPasteColumnWidths
.Cells(1, 1).PasteSpecial Paste:=xlPasteFormats
.Cells(1, 1).PasteSpecial Paste:=xlPasteValues
End With
End If
End With
'turn off the AutoFilter
If .AutoFilterMode Then .AutoFilterMode = False
'turn off active copy range
Application.CutCopyMode = False
End With
End Sub

Resources