VBA to copy information down but on a loop - vba

I tried to run this in a macro but somehow the loop didn’t work as it kept referencing the cells when I need this to just run down until it hits the end. I am looking to do the following:
If there is a value in column D and nothing in column B then the information need to be copied down. To copy it will look for column A to match but look for the top line of the match so there is values in column B. once it finds the top row the code should copy down rows B,E & H
the code i used is a recorded macro. this look for a value in B goes to the bottom (using Ctrl + Down) copies this value and uses Ctrl + Shift + down, the up one to find the end. pastes the value then moves across to the other columns. but i can only get this to run on the first section it needs to repeat until the end. the end is defined on row 10000:
Sub Sort_The_Fus_To_One_Line_2()
Application.Goto Reference:="R8C2"
Range("B8").Select
Selection.End(xlDown).Select
Selection.Copy
Range(Selection, Selection.End(xlDown)).Select
Range("B10:B43").Select
ActiveSheet.Paste
Range("E10").Select
Application.CutCopyMode = False
Selection.Copy
Range(Selection, Selection.End(xlDown)).Select
Range("E10:E43").Select
ActiveSheet.Paste
Range("H10").Select
Application.CutCopyMode = False
Selection.Copy
Range(Selection, Selection.End(xlDown)).Select
Range("H10:H43").Select
ActiveSheet.Paste
Range("B10").Select
Selection.End(xlDown).Select
End Sub
Can someone help!? Thanks!

I think this is something like what you're after:
Sub Test()
Dim rStart As Range, rEnd As Range
Dim FirstAdd As String
Dim lLastRow As Long
lLastRow = 10001
With Worksheets("Sheet1").Columns(2)
'Find the first non-blank cell in column B.
Set rStart = .Find(What:="*", _
LookIn:=xlValues, _
LookAt:=xlWhole, _
SearchDirection:=xlNext)
'rStart will be Nothing if the column is empty.
If Not rStart Is Nothing Then
FirstAdd = rStart.Address 'Very first found address.
Do
'Find the next non-blank cell in column B.
Set rEnd = .FindNext(rStart)
If rEnd.Row < rStart.Row And rStart.Row < lLastRow Then
'The cell reference is relative to the column in the With command.
'making column 1 = sheet column 2.
Set rEnd = .Cells(lLastRow, 1)
End If
'If the second address isn't the same as the very first address and
'the second address isn't the row below the start address then copy the value down.
If rEnd.Address <> FirstAdd And rStart.Offset(1).Address <> rEnd.Address Then
'Places the value from the Start row into every cell between one below the
'start row to one cell above the end row.
Worksheets("Sheet1").Range(rStart.Offset(1), rEnd.Offset(-1)) = rStart.Value
Worksheets("Sheet1").Range(rStart.Offset(1), rEnd.Offset(-1)).Offset(, 3) = rStart.Offset(, 3).Value
Worksheets("Sheet1").Range(rStart.Offset(1), rEnd.Offset(-1)).Offset(, 6) = rStart.Offset(, 6).Value
End If
'Set the new start address as the previous end address.
Set rStart = rEnd
Loop While rStart.Row < lLastRow
End If
End With
End Sub

Related

Search for header, copy paste value till last row

I have created a macro to search for header and Copy the header and paste it till the last row of that particular column. But when I do it I have to specify the column which i dont want . But I need to paste it with the Header search in the same column till last row. Ex:Total is the Header name in BV column. Please assist.
Range("A1").Select
Dim rngUsernameHeader As Range
Dim rngHeaders As Range
Set rngHeaders = Range("1:1")
Set rngUsernameHeader = rngHeaders.Find(what:="Total", After:=Cells(1, 1))
rngUsernameHeader.Copy
lastrow = Range("A65536").End(xlUp).Row
**ActiveSheet.Paste Destination:=Range("BV1:BV" & lastrow)**
Selection.End(xlUp).Select
Application.CutCopyMode = False
is this what you are looking for?
Range("A1").Select
Dim rngUsernameHeader As Range
Dim rngHeaders As Range
Set rngHeaders = Range("1:1")
Set rngUsernameHeader = rngHeaders.Find(what:="Total", After:=Cells(1, 1))
rngUsernameHeader.Copy
lastrow = Range("A65536").End(xlUp).Row
ActiveSheet.Paste Destination:=Range(rngUsernameHeader, rngUsernameHeader.Offset(lastrow - 1))
Selection.End(xlUp).Select
Application.CutCopyMode = False
Just build your paste range from rngUsernameHeader
ActiveSheet.Paste Destination:=Range( _
Cells(1, rngUsernameHeader.Column), _
Cells(lastrow, rngUsernameHeader.Column))
if you want to paste the content of row 1 cell containing "Total" in the found cell column from row 1 down to the row corresponding to column A last not empty one, then use:
Sub main()
With Range("1:1").Find(what:="Total", After:=Cells(1, 1))
.Resize(Cells(Rows.Count, 1).End(xlUp).Row).Value = .Value
End With
End Sub
or, if you know that "Total" is the whole content of the header
Sub main()
Range("1:1").Find(what:="Total", After:=Cells(1, 1)).Resize(Cells(Rows.Count, 1).End(xlUp).Row).Value = "Total"
End Sub
while, if you want to paste the content of row 1 cell containing "Total" in the found cell column from row 1 down to the last not empty cell of that same column, then use:
Sub main2()
With Range("1:1").Find(what:="Total", After:=Cells(1, 1))
.Resize(Cells(Rows.Count, .Column).End(xlUp).Row).Value = .Value
End With
End Sub

edit data on the sheet from master sheet

I have a file that consists of 5 data related sheets and an additional master sheet. These master sheet has a functionality that retrieves the data from all sheets into master sheet based on the ID.
The flow of the code for now is:
Range().Select
Application.CutCopyMode = False
Selection.Copy
Sheets("mSheet").Select
ActiveSheet.Paste
It does what I want in a simple way that it only shows the necessary data. What I ideally want is to have link between this data and actual sheet, so that once I retrieve data in the master sheet and perform any edit - this edit is made in the actual corresponding sheet.
Any idea and suggestion is appreciated.
Original partial VBA code:
Sheets("dSheet1").Select
ActiveSheet.ListObjects("Table").Range.AutoFilter Field:=3, Criteria1:=id
Range(Selection, Selection.End(xlDown)).Select
Range(Selection, Selection.End(xlToRight)).Select
Application.CutCopyMode = False
Selection.Copy
Sheets("masterSheet").Select
ActiveSheet.Paste Destination:=Sheets("masterSheet").Range("A8")
The following will copy the contents of your selection, but instead of pasting the value will enter the reference to the cell, so when the cell gets updated, so would the master sheet:
Sub foo()
Range("A1").Select
Sheets("mSheet").Range("A2").Formula = "=" & Selection.Address
End Sub
EDIT
The following will do the opposite of the code above, so it will copy the data, paste it in your master Sheet and then go back to the original copied range and enter the cell reference there so when the master is updated, so is that range:
Sub foo()
Range("A1").Select 'select the range to be copied
Application.CutCopyMode = False
Selection.Copy 'copy it
Sheets("mSheet").Range("A2").PasteSpecial (xlPasteValues) 'paste the value into your master sheet
Range("A1").Formula = "=" & Sheets("mSheet").Range("A2").Address
'go back to your previous selection and enter the formula to reference the specific cell
End Sub
UPDATE
Replace your code with the following, as it does the same but with fewer lines of code an without any Select statement:
Sub foo2()
Sheets("dSheet1").ListObjects("Table").Range.AutoFilter Field:=3, Criteria1:=ID
Sheets("dSheet1").Range("A2:D2").Copy Destination:=Sheets("masterSheet").Range("A8")
'change the range above to copy as many columns as you need
End Sub
Then add the following code behind you Master Sheet to detect changes:
Private Sub Worksheet_Change(ByVal Target As Range)
'place this code behing the masterSheet
LastRow = Sheets("dSheet1").Cells(Sheets("dSheet1").Rows.Count, "A").End(xlUp).Row
'get the last row of dSheet1
If Target.Address = "$A$8" Then 'if A8 changes
For i = 1 To LastRow 'loop through dSheet1 to find the ID
If Sheets("dSheet1").Cells(i, 3) = Range("C8").Value Then 'when ID found
Sheets("dSheet1").Cells(i, 1) = Range("A8").Value 'change relevant cell with new data
End If
Next i
End If
'below do the same as above to change data for other columns, add more to adapt it to be able to make changes to however many columns you are copying over
If Target.Address = "$B$8" Then
For i = 1 To LastRow
If Sheets("dSheet1").Cells(i, 3) = Range("C8").Value Then
Sheets("dSheet1").Cells(i, 2) = Range("B8").Value
End If
Next i
End If
If Target.Address = "$D$8" Then
For i = 1 To LastRow
If Sheets("dSheet1").Cells(i, 3) = Range("C8").Value Then
Sheets("dSheet1").Cells(i, 4) = Range("D8").Value
End If
Next i
End If
End Sub
This assumes that your ID's are unique and you are getting a single row as a result of your autofilter.
How about adding buttons.
CopyID - Copy range from SheetID = B1
After Editing, EditID - will clear range in SheetID = B1, and rewrites everything from mSheet Range from A3 then paste in SheetID = B1
Edit ID code:
Public Sub EditID(ID As String)
Sheets(ID).Select
If Range("A1").Value <> "" Then
Range("A1").Select
Range(Selection, Selection.End(xlToRight)).Select
Range(Selection, Selection.End(xlDown)).Select
Selection.Clear
End If
Sheets("mSheet").Select
If Range("A3").Value <> "" Then
Range("A3").Select
Range(Selection, Selection.End(xlToRight)).Select
Range(Selection, Selection.End(xlDown)).Select
Selection.Copy
Sheets(ID).Select
Range("A1").Select
ActiveSheet.Paste
End If
Sheets("msheet").Select
End Sub

Filtering Excel with VBA and exiting Sub if there is no result

I have some code that filters a large data set, then selects visible cells, and copy & pastes the range elsewhere.
Sub Filterstuff()
' Select & Filter data
Sheets("Main").Select
Lastrow = ActiveSheet.Range("A2").End(xlDown).Row
Range("A1").Select
Range(Selection, Selection.End(xlToRight)).Select
Range(Selection, Selection.End(xlDown)).Select
Selection.AutoFilter
' Filter for things
ActiveSheet.Range("A1:AU" & Lastrow).AutoFilter Field:=39, Criteria1:="words"
ActiveSheet.Range("A1:AU" & Lastrow).AutoFilter Field:=43, Criteria1:= _
"<>*wordswords*"
' Find the first unfiltered cell
Range("A1").Select
ActiveCell.Offset(1, 0).Select
Do Until ActiveCell.EntireRow.Hidden = False
ActiveCell.Offset(1, 0).Select
Loop
' If there are no unfiltered cells, exit
If ActiveCell.Row = Lastrow + 1 Then
Exit Sub
' Else paste results normally
Else
Range(Selection, Selection.Offset(0, 47)).Select
Range(Selection, Selection.End(xlDown)).Select
Selection.SpecialCells(xlCellTypeVisible).Select
Selection.Copy
' Paste to bottom
Sheets("PasteSheet").Select
countrows = Cells(Cells.Rows.Count, "A").End(xlUp).Row
Range("A" & countrows + 1).Select
ActiveSheet.Paste
End If
' Return to Main and unfilter
Sheets("Main").Select
Cells.Select
ActiveSheet.ShowAllData
Selection.AutoFilter
End Sub
My issue is located in the code block meant to exit the sub if everything gets filtered out and there are no resulting rows with data after filtering. The relevant code begins at the commented section "Find the first unfiltered cell".
This code finds the first unhidden row, and checks if it is after the last row of data in the data set. My issue is that it is exceedingly slow. My data set can be 100,000+ rows and looping through it using ActiveCell.Offset(1, 0).Select takes forever.
How can I re-tool this code to exit the sub if everything gets filtered out?
Avoid using Select (this will improve the runtime performance):
http://stackoverflow.com/questions/10714251
Then, get a handle on the full range of "data". Finally, after applying autofilter, check the range's SpecialCells(xlCellTypeVisible).Count.
As long as that .Count is greater than the number of columns in your range, then you have at least one visible row of data (assuming your data has headers -- if there are no headers, you can just compare whether > 0).
Untested:
Sub Filterstuff()
' Select & Filter data
Dim ws as Worksheet
Dim rng as Range
Set ws = Worksheets("Main")
Set rng = ws.Range("A2:AU" & ws.Range("A2").End(xlDown).Row))
rng.AutoFilter
' Filter for things
rng.AutoFilter Field:=39, Criteria1:="words"
rng.AutoFilter Field:=43, Criteria1:="<>*wordswords*"
' Find the first unfiltered cell
If rng.SpecialCells(xlCellTypeVisible).Count > rng.Columns.Count Then
'Autofilter has returned at least one row of data
Else
MsgBox "No data results from Autofilter"
Exit Sub
End If
<more code...>

Run time error in excel vba. last used range rows selects row 65536 instead of actual last used range

I am trying to select columns E and K from sheet Input, process in Working sheet and paste in the Output sheet after the last used row. I have stored the last used row number in x and paste the values in x+1 cell. However excel selects last row of the sheet (x as 65536) and gives run time error 4004. Can someone please help me in assisting the code.
Dim x As Long, y As String
Sheets("Input").Activate
Range("E:E,K:K").Select
Range("K1").Activate
Selection.Copy
Sheets("Working").Select
Cells(1, 1).Select
ActiveSheet.Paste
Cells.Select
Application.CutCopyMode = False
Selection.AutoFilter
Range("B5").Select
ActiveSheet.Range("$A$1:$H$30").AutoFilter Field:=1, Criteria1:="="
Cells.Select
Selection.Delete Shift:=xlUp
Columns("B:B").Select
Selection.Insert Shift:=xlToRight, CopyOrigin:=xlFormatFromLeftOrAbove
Range("B2").Select
ActiveCell.FormulaR1C1 = _
"=IF(RC[-1]="""","""",VLOOKUP(RC[-1],Repository!C[-1]:C[1],3,0))"
Range("B2").Select
Selection.Copy
Range("B3:B30").Select
ActiveSheet.Paste
Cells.Select
Application.CutCopyMode = False
Selection.Copy
Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=False
Range("B2:C2").Select
Range(Selection, Selection.End(xlDown)).Select
Application.CutCopyMode = False
Selection.Copy
Sheets("Output").Select
Range("A1").Select
x = Worksheets("Output").UsedRange.Rows.Count
y = "a" & Trim(x + 1)
ActiveSheet("Output").Range(y).Select
ActiveSheet.Paste
Your UsedRange is still thinking that the last row is 65536. Add this subroutine, then call it right before you set x.
Sub CorrectUsedRange()
Dim values
Dim usedRangeAddress As String
Dim r As Range
'Get UsedRange Address prior to deleting Range
usedRangeAddress = ActiveSheet.UsedRange.Address
'Store values of cells to array.
values = ActiveSheet.UsedRange
'Delete all cells in the sheet
ActiveSheet.Cells.Delete
'Restore values to their initial locations
Range(usedRangeAddress) = values
End Sub
Near the bottom of your code replace:
Sheets("Output").Select
with:
Sheets("Output").Select
ActiveSheet.UsedRange
this should "re-set" UsedRange
Sometimes the Used Range gets generically large and won't reset on it's own. When this happens, the only way that I've found to force it to reset itself correctly is to Save the Workbook that the subject Worksheet is in. This works for me, on Excel 2010 anyway. Since you're using .Select and Active<obj> (which I don't recommend), it would simply be this:
ActiveWorkbook.Save
I would use a Find loop to populate an array and then output the array when the macro has completed. There is no need for a "Working" sheet. This also uses Cells(Rows.Count, "A").End(xlUp) in order to find the last populated row instead of UsedRange.Rows.Count which can be unreliable.
Sub tgr()
Dim rngFound As Range
Dim rngLookup As Range
Dim arrResults() As Variant
Dim ResultIndex As Long
Dim strFirst As String
With Sheets("Input").Columns("E")
Set rngFound = .Find("*", .Cells(.Cells.Count), xlValues, xlWhole)
If Not rngFound Is Nothing Then
strFirst = rngFound.Address
ReDim arrResults(1 To WorksheetFunction.CountA(.Cells), 1 To 2)
Do
If rngFound.Row > 1 Then
ResultIndex = ResultIndex + 1
On Error Resume Next 'Just in case the VLookup can't find the value on the 'Repository' sheet
arrResults(ResultIndex, 1) = Evaluate("VLOOKUP(""" & rngFound.Value & """,Repository!A:C,3,FALSE)")
arrResults(ResultIndex, 2) = .Parent.Cells(rngFound.Row, "K").Value
On Error GoTo 0 'Remove the On Error Resume Next condition
End If
Set rngFound = .Find("*", rngFound, xlValues, xlWhole)
Loop While rngFound.Address <> strFirst
End If
End With
If ResultIndex > 0 Then Sheets("Output").Cells(Rows.Count, "A").End(xlUp).Offset(1).Resize(ResultIndex, UBound(arrResults, 2)).Value = arrResults
Set rngFound = Nothing
Erase arrResults
End Sub
instead of used range check how many rows already are filled with this code:
X = WorksheetFunction.CountA(Columns(1))
Of course this only works ok if you have no rows that are empty in Column A, as those rows would be ignored!

how to capture cell address as a variable and use in VB code?

Need a code snippet; if some kind guru could help, please. I need to express the following cursor movement sequence in XL VBA.
After entering a formula in cell A1 (Col-A is otherwise empty), I need to copy the formula to all cells in the range A1:AN, where N is the last row of the table.
I recorded a macro to do the following (code below):
1) enter the formula (in Cell A1)
2) copy the formula
3) go Right to B1
4) go to the last populated cell in Col-B [using Ctrl+Down] (easiest way to find the last row)
5) go Left to Col-A
6) select all cells from current to A1
7) paste the formula to the selection
The part I need help with is a way to capture the cell address in step 5 as a variable so that I can use this macro on a series of files having a variable number of rows.
Here is the recorded macro. In this example, the last row in the table is 7952.
Sub test()
ActiveCell.FormulaR1C1 = "=LEFT(RC[1],3)"
ActiveCell.Select
Selection.Copy
ActiveCell.Offset(0, 1).Range("A1").Select
Selection.End(xlDown).Select
ActiveCell.Offset(0, -1).Range("A1").Select
Range(Selection, Selection.End(xlUp)).Select
ActiveCell.Offset(-7951, 0).Range("A1:A7951").Select
ActiveCell.Activate
ActiveSheet.Paste
End Sub
Kindly copy the below code to the worksheet.
Private Sub Worksheet_Change(ByVal Target As Range)
Application.EnableEvents = False
Application.Calculation = xlCalculationManual
If Target.Address = "$A$1" And Target.Count = 1 And Target.HasFormula Then
Dim lastRow As Long
lastRow = Range("A65000").End(xlUp).Row
Dim rng As Range
Set rng = Range("A2:A" & lastRow)
' Target.Copy
' rng.PasteSpecial xlPasteFormulas
'OR
' rng.Formula = Target.Formula
' OR
rng.FormulaR1C1 = Target.FormulaR1C1
End If
Application.EnableEvents = True
Application.Calculation = xlCalculationAutomatic
End Sub
I'm not sure if your end cell is always going to be the same, meaning you may want to "un" hard code the rows, but you could try this.
Sub test()
Range(Cells(1, 1), Cells(7951, 1)) = "=LEFT(RC[1],3)"
End Sub
If you are always going to put equations in column A based on the number of rows used in column B you could try this.
Sub test()
' dimension the variable type
Dim lastRow As Long
' select cell "B1"
Cells(1, 2).Select
' jump to the last consecutive row in column B
Selection.End(xlDown).Select
' collect the row number into a variable
lastRow = ActiveCell.Row
' paste the equation into the variable length range
Range(Cells(1, 1), Cells(lastRow, 1)) = "=LEFT(RC[1],3)"
End Sub
Thanks Todd and user2063626,
I decided on a simpler approach. I only needed to obtain the last row in order to set my selection area; the number of the last row is not used in the actual values to be written. The files to be manipulated are flat ascii exports; the column layout is constant, only the number of rows is variable.
After writing the formula to A1, I move down column B and test for a value one cell at a time; if TRUE, copy the formula to the left adjacent cell; if FALSE, end process.
Sub FillClientCodes()
Range("A1").Select
ActiveCell.FormulaR1C1 = "=LEFT(RC[1],3)"
ActiveCell.Select
Selection.Copy
ActiveCell.Offset(0, 1).Select
ActiveCell.Offset(1, 0).Select
CheckCell:
ActiveCell.Activate
If ActiveCell.Value <> 0 Then
ActiveCell.Offset(0, -1).Select
ActiveCell.Activate
ActiveSheet.Paste
ActiveCell.Offset(0, 1).Select
ActiveCell.Offset(1, 0).Select
GoTo CheckCell
Else: GoTo EndOfData
End If
EndOfData:
End Sub
It's not elegant - it runs slower than a single select and paste - but it works, and it will work on all the files I need to process. Thanks again.