Dynamic Last Row - vba

I'm using the following code to find the last row in Sheet1 & Sheet2
Dim 1LastRow As Long
Dim 2LastRow As Long
1LastRow = Sheets("Sheet1").Cells(Rows.Count, 1).End(xlUp).Row
2LastRow = Sheets("Sheet2").Cells(Rows.Count, 1).End(xlUp).Row
This works as initially expected, however I'd like "1LastRow" to adjust to dynamically pick up the last row.
For example, if Sheet1 initially has 50 rows, but after:
Sheets("Sheet2").Range("B2:B" & 2LastRow).Copy
Sheets("Sheet1").Range("D2").PasteSpecial xlPasteValues
Sheet1 now has 55 rows. However, "1LastRow" is still assuming that Sheet1 only has 50 rows, and thus creates inconsistencies throughout the rest of the code when "1LastRow" is used. Any input is greatly appreciated!

Well, a variable doesn't magically update itself. You see that you have code that pastes rows. Just repeat the code that sets the variable for the last row when you do something that changes the last row.
Dim 1LastRow As Long
Dim 2LastRow As Long
1LastRow = Sheets("Sheet1").Cells(Rows.Count, 1).End(xlUp).Row
2LastRow = Sheets("Sheet2").Cells(Rows.Count, 1).End(xlUp).Row
Sheets("Sheet2").Range("B2:B" & 2LastRow).Copy
Sheets("Sheet1").Range("D2").PasteSpecial xlPasteValues
1LastRow = Sheets("Sheet1").Cells(Rows.Count, 1).End(xlUp).Row
Or you could use a dynamic range name with a formula along the lines of
=INDEX(Sheet1!$A:$A,COUNTA(Sheet1!$A:$A))
Then refer to that range like
ThisWorkbook.Worksheets("Sheet1").Range("OneLastRow").Row
But this will fall over if the column contains blank cells.

first off your code won't compile in an Office VBA environment: variables names can't start with a number. So let's call them LastRow1 and LastRow2, instead
second, even if there were some automagic way of making a variable self update, your code would fail since you are copying a 1-column range from Sheet2 and pasting it starting from cell D2 of Sheet1, which would have no effect whatsoever in column A last row index setting
all that said, you have could use a Function() that returns the wanted row index.
For instance this function:
Function LastRow1() As Long
With Sheets("Sheet1")
LastRow1 = .Cells(.Rows.Count, 4).End(xlUp).Row
End With
End Function
would return Sheet1 column D last not empty row index (well, it would actually fail should column A be totally empty, but it's an easy fix)
you could easily have it return any other column last not empty cell row index by simply changing column index in .Cells(.Rows.Count, 4)
so that your "main" code would look like:
Sub main()
Dim LastRow2 As Long
LastRow2 = Sheets("Sheet2").Cells(Rows.Count, 1).End(xlUp).Row
Sheets("Sheet2").Range("B2:B" & LastRow2).Copy
Sheets("Sheet1").Range("D2").PasteSpecial xlPasteValues
MsgBox LastRow1 ' this would return Sheet1 column D last not empty row index
MsgBox Sheets("Sheet1").Range("D2:D" & LastRow1).Address 'this would return the address of Sheet1 column D range from cell D2 down to last not empty cell
End Sub

Related

Excel VBA macro to copy rows that meet a criteria to a new row on another sheet

I am struggling with my code- and will admit that I am a newbie to VBA.
Yes- I have reviewed a ton of posts and dont seem to be able to fix my code!
I am trying to extract rows that meet a specific value ('Priority')- which is located in column H across 15 sheets. The code needs to extract any rows that meet the criteria from all of the 15 sheets. The extracted row is to be pasted into the front sheet "FootpathStrategyTool"- starting at cell A20 (below the predefined headers).
I am having difficulty getting the loop to paste the copied row into the next blank row. The code runs, but seems to miss the first few worksheets. It may have something to do with my final row line.
If anyone can help I would be extremely grateful!!!!
Sub getdata()
'1. what is the priority value you wish to search for?
'2. define lastrow for the sheets
'3. Find records that match priority value and paste them into the main sheet
Dim Priority As Integer
Dim Worksheet As Worksheet
Priority = Sheets("FootpathStrategyTool").Range("I10").Value
' Find the final row of data for all sheets
For Each Worksheet In ActiveWorkbook.Worksheets
Worksheet.Select
finalrow = Cells(Rows.Count, 1).End(xlUp).Row
Dim NextRow As Range
Set NextRow = Sheets("FootpathStrategyTool").Cells(Cells.Rows.Count,
1).End(xlUp).Offset(1, 0)
' Loop through each row
For x = 4 To finalrow
' Decide if to copy based on column H Priority
ThisValue = Cells(x, 8).Value
If ThisValue = Priority Then
Cells(x, 1).Resize(1, 33).Copy
Sheets("FootpathStrategyTool").Select
NextRow = Cells(“A” & Rows.Count).End(xlUp).Row + 19
Cells(NextRow, 1).Select
End If
' Step down 1 row from present location.
ActiveCell.Offset(1, 0).Select
ActiveSheet.Paste
Sheets("FootpathStrategyTool").Select
Next x
Next Worksheet
End Sub
A few suggestions I hope you find helpful:
Dim Worksheet As Worksheet. Typically you wouldn't want to declare a variable name with the same name as an object. It can make things a bit confusing. I'd suggest Dim wsData As Worksheet.
ThisValue = Cells(x, 8).Value. While this works, you haven't explicitly declared ThisValue with a type which means ThisValue is now a Variant type. In the example above, you have a simple evaluation of "If ThisValue = Priority Then" so everything in this code will probably work as expected, but you might get unexpected results or errors in a more complex application. The same goes for "finalrow" and "x".
There 's a significant difference between "Cells" and ".Cells". ".Cells" refers to a specific worksheet that you defined earlier in the code while "Cells" refers to whichever worksheet was last active. Since your code is trying to create row numbers while jumping back and forth between the front sheet and the data sheets, it's easy to lose track of which sheet you're actually creating the numbers from.
When referring to specific worksheets and ranges, it's not always necessary to jump between them and activate them. You can just move the data as an array by specifying the source and destination locations.
I believe, however, the source of your problem is
NextRow = Cells(“A” & Rows.Count).End(xlUp).Row + 19. You established the last row range when you Declared and Set it above, and now your re-establishing it then adding another 19 rows to it. Also, Cells(“A” & Rows.Count) is not a valid reference. "A" will return as empty which means you're actually saying Cells(Rows.Count) and not providing a column reference.
Please consider this as an alternative.
Sub getdata()
'1. what is the priority value you wish to search for?
'2. define lastrow for the sheets
'3. Find records that match priority value and paste them into the main sheet
'This identifies precisely which sheet you're copying data to.
Dim wsHome As Worksheet: Set wsHome = ActiveWorkbook.Worksheets("FootpathStrategyTool")
Dim Priority As Integer: Priority = wsHome.Range("I10").Value
Dim wsData As Worksheet
Dim ThisValue As Variant
Dim NextRow As Variant
Dim finalrow As Long
'i0 will be used to cycle through the data worksheets.
Dim i0 As Long
'i1 will be used to increment through the rows on the Home sheet starting at row 20.
Dim i1 As Long: i1 = 20
'This says to start at the second worksheet and look at each one to the end.
For i0 = 2 To ActiveWorkbook.Worksheets.Count
'This specifies exactly which worksheet you're working with.
With ActiveWorkbook.Worksheets(i0)
finalrow = .Cells(.Rows.Count, 1).End(xlUp).Row
'Loop through each row
For x = 4 To finalrow
'Decide if to copy based on column H Priority. Pull the whole range as an object.
If .Cells(x, 8).Value = Priority Then
ThisValue = .Range(.Cells(x, 1), .Cells(x, 33)).Value
'Identify the data's destination on the Home tab.
Set NextRow = wsHome.Cells(i1, 1)
'Resize the destination range and drop the data in one line.
NextRow.Resize(1, UBound(ThisValue, 2)).Value = ThisValue
'Increment the cell reference to the next row.
i1 = i1 + 1
End If
Next x
End With
Next i0
End Sub

Copy paste values from one sheet to another via loop

Maybe somebody can help me with this one very basic problem that I have which is driving me nuts. I just need to copy some data from one sheet to another one depending on its output.
The variables have been defined previously and wMod and wRes stand for worksheets, countP and countC stands for the total amount of cells calculated in another worksheet.
What I need to do is to write a code that would be able to copy data with the value in cell B which has the number 1 in it, paste and paste it in another sheet. When I write the code below though, I only get in column A, sheet wRes the total amount of cells that were stored in variable countP.
You can see what kind of output I get below with the picture I attached.Data output Anybody has any idea why I cannot get the right output? Been stuck with this problem for hours.
For i = 3 To countP
If wMod.Range("B" & i) = 1 Then
For j = 3 To countC1
wMod.Range("A" & i).Copy wRes.Range("A" & j)
Next j
End If
Next i
The following code will check the Last Row with Data on Sheet1 Column A, then it will loop from row 2 to the Last and check if Column B = 1, then it will check the last row with data on Sheet2 Column A, and paste the value in the next row (ie. next empty row).
It goes like so:
Sub foo()
Dim ws As Worksheet: Set ws = Sheets("Sheet1")
Dim ws2 As Worksheet: Set ws2 = Sheets("Sheet2")
Dim LastRow As Long
Dim LastRow2 As Long
LastRow = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row
'find the last row with data on Column A in Sheet1
For i = 2 To LastRow 'loop from row 2 to last
If ws.Range("B" & i) = 1 Then 'if column B in Sheet1 = 1 then
LastRow2 = ws2.Cells(ws2.Rows.Count, "A").End(xlUp).Row 'find the last row with data on Sheet2 Column A
ws2.Cells(LastRow2 + 1, 1) = ws.Range("A" & i)
End If
Next i
End Sub

copy and paste entire row from one to another sheet issue

I would like to copy entire row from today Sheet to last available row of main Sheet if today Sheet B2:B cell value = "Update". However, I come up with copy area and paste area are not the same size issue. I've tried many ways but cannot get it solved. Can anyone help? Thanks in advanced.
Sub getnew()
Dim Sheet1 As Worksheet
Dim Sheet3 As Worksheet
Dim lastrow As Integer
Dim i As Integer
Set Sheet1 = ThisWorkbook.Sheets("main")
Set Sheet3 = ThisWorkbook.Sheets("today")
lastrow = Sheet3.Range("C" & Rows.Count).End(xlUp).Row
Dim erow As Long
For i = 2 To lastrow
erow = Sheet1.Range("C" & Rows.Count).End(xlUp).Row + 1
If Sheet3.Cells(i, 2).Value = "Update" Then
Sheet3.Cells(i, 2).EntireRow.Copy Destination:=Sheet1.Range("B" & erow)
End If
Next i
End Sub
You are trying to copy the entire row of a worksheet, but you are pasting that row in another sheet, starting at column B. They aren't the same size, because an entire row contains 16,384 columns, but a row starting at column B only has 16,383 columns.
You have a few options to resolve this:
You could paste the entire row onto the other sheet at column A, like so:
Sheet3.Cells(i, 2).EntireRow.Copy Destination:=Sheet1.Range("A" & erow)
Or you could copy only the number of columns which you care about, instead of trying to copy the entire row, like so [I don't know what it is you're trying to copy, so you will need to figure out how exactly you want to size it. I have assumed that 10 columns is sufficient]:
Sheet3.Range(Cells(i, 2), Cells(i,11)).Copy Destination:=Sheet1.Range("B" & erow)

copy part of a row to another sheet unless already been copied. Source rows can change row

I basically need to copy rows from one sheet to another. The rows which I need to copy are on sheet 2 , columns A to N and I need to copy the rows that has the unique value in column N.
Column N will just be =M1 or =M2 depending on which row has data on it.
Hence I will use a worksheet calculate event to try capture this.
The tricky part is that each time new values exist in say the last row on sheet 2 and then N gets filled form M. I don't want the previously copied rows to be copied. It may also be the case that the entire contents of the rows change places or that one of the rows disappears and the other row will fill its gap on sheet 2. So I need to avoid the calculate event recognizing this from the =M line. i.e if it has already been copied but gets recalcuated - -I don't need it to be copied again.
I guess one way to do this would be toi lookup if the N column value exists in the N column value on sheet 1. Because if that row disappears then it will be on the sheet 1. it will because I have other formulas putting it there.
My starting point for dong this is the code below and I have this set in the worksheet code of sheet 1
Private Sub Worksheet_Calculate()
Dim i As Long
Dim lr1 As Long, lr2 As Long
Dim Delta As String
Dim wks1 As Worksheet, wks2 As Worksheet
Set wks1 = ActiveSheet
Set wks2 = Worksheets("Sheet2") 'change to suit
lr1 = wks1.Cells(Rows.Count, "N").End(xlUp).Row
For i = 2 To lr1
lr2 = wks2.Cells(Rows.Count, "A").End(xlUp).Row + 1
wks1.Cells(i, "N").EntireRow.Copy Destination:=wks2.Cells(lr2, "A")
Next i
End Sub
I also have this working which I would need to incorporate into that worksheet calcualte
Sub updt()
Dim sh1 As Worksheet, sh2 As Worksheet, lr As Long, rng As Range
Set sh1 = Sheets(1) 'Edit sheet name
Set sh2 = Sheets(2) 'Edit sheet name
lr = sh1.Cells(Rows.Count, 1).End(xlUp).Row
Set rng = sh1.Range("A2:A" & lr)
For Each c In rng
If WorksheetFunction.CountIf(sh2.Range("A:A"), c.Value) = 0 Then
sh2.Range("A" & sh2.Cells(Rows.Count, 1).End(xlUp).Row)(2) = c.Value
End If
Next
End Sub
The fastest method is pretty sure to copy all wanted rows an then call the remove dublicates function which is almost instantanios. You can simply set your unique col as indicator

Copy filtered cells to another sheet

I have four rows of information that I filter. (month, Name, Service, units) I filter the information by name and month. (depending on information needed) I have put together the following to try and grab the top row of filtered data and place it on another sheet.
Sub Mine()
Dim sht1 As Worksheet
Dim sht2 As Worksheet
Dim lRow As Long
Set sht1 = ThisWorkbook.Sheets("Export")
Set sht2 = ThisWorkbook.Sheets("DB1")
lRow = sht1.Cells(Rows.Count, "A").End(xlUp).Row + 1
sht1.Range("b" & lRow) = sht2.Range("A2" & Cells.SpecialCells(xlCellTypeLastCell).Row).SpecialCells(xlCellTypeVisible) 'Month
sht1.Range("a" & lRow) = sht2.Range("E2" & Cells.SpecialCells(xlCellTypeLastCell).Row).SpecialCells(xlCellTypeVisible) 'Client Name
sht1.Range("c" & lRow) = sht2.Range("C2" & Cells.SpecialCells(xlCellTypeLastCell).Row).SpecialCells(xlCellTypeVisible) 'Service
sht1.Range("d" & lRow) = sht2.Range("H1" & Cells.SpecialCells(xlCellTypeLastCell).Row).SpecialCells(xlCellTypeVisible) 'Units
End Sub
This does not error out, it just doesn't copy anything to the "export" sheet. The only way I can get anything to copy from one sheet to another is to take the specialcells out. As follows...
Sub Mine()
Dim sht1 As Worksheet
Dim sht2 As Worksheet
Dim lRow As Long
Set sht1 = ThisWorkbook.Sheets("Export")
Set sht2 = ThisWorkbook.Sheets("DB1")
lRow = sht1.Cells(Rows.Count, "A").End(xlUp).Row + 1
sht1.Range("b" & lRow) = sht2.Range("A2") 'Month
sht1.Range("a" & lRow) = sht2.Range("E2") 'Client Name
sht1.Range("c" & lRow) = sht2.Range("C2") 'Service
sht1.Range("d" & lRow) = sht2.Range("H1") 'Units
End Sub
But as said prior, it only copies the first line, filtered or not. My goal is to obtain the filtered top line of "DB1" and copy it to sequenced cells in "Export". Any assistance would be greatly appreciated.
-JGr3g
Okay, so I don't think your code is doing what you think it is.
Lets take some apart
sht2.Range("A2" & Cells.SpecialCells(xlCellTypeLastCell).Row).SpecialCells(xlCellTypeVisible)
You're calling sht2.Range() and passing it a string. That string is the concatenation of "A2" and Cells.SpecialCells(xlCellTypeLastCell).Row).SpecialCells(xlCellTypeVisible).
What is Cells.SpecialCells(xlCellTypeLastCell).Row).SpecialCells(xlCellTypeVisible)? Well implicitely Cells is a range representing every cell in the active spreadsheet, ThisWorkbook.ActiveSheet.Cells. Then you're asking for SpecialCells(xlCellTypeLastCell) and taking it's row? What happened to sht1 and sht2? You aren't using them, you're using the active sheet?
So if the last cell of the active sheet is in row 50, you are asking for "A2" & 50 which would get "A250", which would be blank since the last used row is 50...
First things first, I'd recommend you avoid using string concatenation for finding cells. None of this "A" & numericVariable stuff. Use something more direct like Cells(numericVariable, 1). Excessive string manipulation will likely hurt you. The cells property can take a row and a column as parameters.
Is your filered range a table/listobject or just a plain old range with an AutoFilter on it? If it's a listobject, get the listobject, get it's DataBodyRange, use .SpecialCells(xlCellTypeVisible) on that, THEN get the first row of the resulting range.
Similarly if it's an autofiltered range, get the target data range, the entire data range. Once you have that, grab the visible cells, then get the first row.
Edit: Untested example
set targetRow = ws.Range(ws.Cells(2, 1), ws.Cells(lRow, 5)).SpecialCells(xlCellTypeVisible).Row(1)
targetSheet.Range("A2") = targetRow.Cells(1,2)
Okay, so what's this doing? I'm taking a worksheet variable ws and getting a range on it. .Range(ws.Cells(2, 1), ws.Cells(lRow, 5)). That range, is from the second row, first column, aka "A2". It goes to the row number contained in lRow on the 5th column, aka "E" & lRow.
From there it gets only the visible cells .SpecialCells(xlCellTypeVisible) then gets the first row of the first area in the selection .Row(1)
Once we have that first row, we can get different columns from it with either .Cells or .Column.