First blank ("") cell in column with IF formula - vba

I have a macro that exactly copies one sheet's data into another.
Sub QuickViewRegMgmt()
("Reg Management").Select
Cells.Select
Selection.Copy
Sheets("Quick View Reg Mgmt").Select
Cells.Select
ActiveSheet.Paste
End Sub
I would like for this macro to also go to the last non-blank cell in Column C (or first blank, I really don't care either way). I tried simple end/offset code, e.g.
Range("A1").End(xldown).Offset(1,0).Select
My problem, however, is that the direct copy macro also copies the underlying formulas, which for Column C is an IF formula. Therefore, no cell in the column is actually empty, but rather they all have an IF formula resulting in a true/false value (respectively, a "" or VLOOKUP).
=IF(VLOOKUP('Reg Management'!$Y260,'Reg Guidance'!$A:$V,3,FALSE)=0,"",VLOOKUP('Reg Management'!$Y260,'Reg Guidance'!$A:$V,3,FALSE))
That means the end/offset code goes to the last cell in the column with the formula (C1000) instead of going to the first cell that has a value of "" (which is currently C260).
What code can I add to this macro to select the first cell that contains an IF formula resulting in a value of "" ---- which has the appearance of being blank?

After trying to be fancy with SpecialCells(), or using Find() or something I couldn't get it ...so here's a rather "dirty" way to do it:
Sub test()
Dim lastRow As Long, lastFormulaRow As Long
lastRow = Range("A" & Rows.Count).End(xlUp).Row
Dim i As Long
For i = lastRow To 1 Step -1
If Cells(i, 1).Formula <> "" And Cells(i, 1).Value = "" Then
lastFormulaRow = i
Exit For
End If
Next i
End Sub
Edit2: Here's one using .SpecialCells(). Granted I think we can whittle this down more, I like it better:
Sub lastRow()
Dim tempLastRow As Long
tempLastRow = Range("C" & Rows.Count).End(xlUp).Row
Dim lastRow As Range
Set lastRow = Columns(3).SpecialCells(xlCellTypeFormulas).Find(What:="", LookIn:=xlValues, LookAt:=xlWhole, searchdirection:=xlPrevious, after:=Range("C" & tempLastRow))
Debug.Print lastRow.Row
End Sub
It returns 10 as the row.
Edit: Be sure to add the sheet references before Range() and Cells() to get the last row. Otherwise, it's going to look at your active sheet to get the info.

Related

how to copy cells from sheet 1 to sheet 2 without removing data on sheet 2

I need code, as my title suggests, for the following task. I already tried a lot of different code but it's still not working.
I only need to move 2 columns, "SKU" and "Discount", into sheet2 using command button and delete it right away.
I'm already okay for this coding. However, but the problem is just beginning.
When I succeed to moved the first data, and try to move the 2nd data, the 1st data disappears.
I already tried many ways but still can't figure it out what's wrong with the code.
Please check the following code:
Sub OUTGOING_GOODS()
function1
function2
clear
Range_End_Method
End Sub
Sub function1()
Sheets("Invoice Print").Range("B21:B27").Copy Destination:=Sheets("Outgoing Goods").Range("D4")
End Sub
Sub function2()
Sheets("Invoice Print").Range("D21:D27").Copy Destination:=Sheets("Outgoing Goods").Range("L4")
End Sub
Sub clear()
Range("B21:B27").clear
End Sub
I also need to change the range for input data as well. As you can see the Range is defined only from D21:D27, but I need more than row 27 just in case there is additional data inputted.
Already tried the following code:
With Worksheets("Sheet2")
LastRow = .Cells(.Rows.Count, "D").End(xlUp).Row
LastRow = .Cells(.Rows.Count, "L").End(xlUp).Row
For Each cell In Range("D4:D" & LastRow)
DestinationRow = LastRow + 1
Next
For Each cell In Range("L4:L" & LastRow)
DestinationRow = LastRow + 1
Next
End With
And
Lastrow = .Cells(.Rows.Count, 1).End(xlUp).Row
For i = 1 To InputData
Lastrow = .Cells(.Rows.Count, 1).End(xlUp).Row
For j = 1 To 3
.Cells(lastrow + 1, j).Value = InputData(i, j)
Next j
Next i
End With
This still isn't working.
Based on our discussions thus far I'd suggest the following:
Sub Outgoing_Goods_New()
'
Dim Outgoing As Worksheet 'Generally it's better to use Worksheet variables. Saves the trouble of having to re-type the sheet name each time you reference the sheet
Dim Invoice As Worksheet
Dim LastRow_Invoice As Long
Dim LastRow_Outgoing As Long
Set Outgoing = ActiveWorkbook.Worksheets("Outgoing Goods")
Set Invoice = ActiveWorkbook.Worksheets("Invoice Print")
'Find the last row of Outgoing column D that's used so we know where to paste the new set of outgoing goods
LastRow_Outgoing = Outgoing.Range("D1048576").End(xlUp).Row
'Make sure column L of Outgoing ends at the same point
If Outgoing.Range("L1048576").End(xlUp).Row > LastRow_Outgoing Then
LastRow_Outgoing = Outgoing.Range("L1048576").End(xlUp).Row
End If 'else column L's last used row is farther up the worksheet or the same row. Either way no need to update the value
'Determine how much data to copy
LastRow_Invoice = Invoice.Range("B1048576").End(xlUp).Row 'I'm assuming Column D of Invoice Print has to end at the same row. If not, use the same IF statement as above, but
'checking column D of Invoice
'Copy the data from column B
Invoice.Range("B2:B" & LastRow_Invoice).Copy
'Paste to Outgoing Goods
Outgoing.Range("B" & LastRow_Outgoing).PasteSpecial xlPasteAll
'Copy Column D of Invoice
Invoice.Range("D2:D" & LastRow_Invoice).Copy
Outgoing.Range("L" & LastRow_Outgoing).PasteSpecial xlPasteAll
'Clear the data from Invoice print
Invoice.Range("B2:B" & LastRow_Invoice).ClearContents 'Removes the Value, but leaves formatting, comments, etc. alone
End Sub
This is mostly the logic you already had, but I did some clean-up to remove ambiguities and genericize the logic a little. Also, notice that I didn't keep the separate Subs. With how little you're doing there's just not any benefit to parsing the logic, especially with none of the code being re-used.
Last, I didn't delete column D on Invoice Print assuming that the cells just held formulas that pull in new data based on the values in Column B. If that's not the case, it seems like you should add a second ClearContents to delete Column D as well, but that's not certain given the vagueness of your use case.

Matching and inserting records in excel

I have two sheets of data. One sheet has Primary Id with 4 fields and other has primary Id with 2 fields.
Sheet A Sheet B
ID Name Price Type Category ID Name Price
1 S Normal 2 Aus 500
2 N Default 1 Ind 400
Basically I need to match the ID of both sheets and copy the corresponding Name and Price in sheet A form Sheet B. I have tried the following code,
Sub Copy()
lastrowA = Worksheets("SheetA").Cells(Rows.Count, "A").End(xlUp).Row + 1
Set rngA = Range("A2" & lastrowA)
lastrowB = Worksheets("SheetB").Cells(Rows.Count, "A").End(xlUp).Row + 1
Set rngB = Range("A2" & lastrowB)
For Each x In rngB
For Each y In rngA
If x.Value() = y.Value Then
' Copy paste name and price form B to A
End If
Next
Next
End Sub
It's never a good idea to use a reserved word as the name of your macro. Particularly so if you plan to use a .Copy operation within the macro.
Sub MyCopy()
Dim lastrowA As Long
With Worksheets("SheetA")
lastrowA = .Cells(Rows.Count, "A").End(xlUp).Row
With .Range("B2:C" & lastrowA)
.Formula = "=IFERROR(VLOOKUP($A2, 'SheetB'!$A:$C, COLUMN(B:B), FALSE), """")"
.Value = .Value
End With
End With
End Sub
That bulk populates the entire region with the appropriate formula without looping then converts the returned values to raw values. Any non-matches will be blank rather than #N/A errors.
Does it have to be done without using formulas? I'm not sure if I'm missing something, but surely you can just use either a Vlookup or an Index Match?
If entering the formula from VBA:
Cells(2,2).FormulaR1C1 = "=INDEX(Sheet2!R2C2:R3C3,MATCH(RC[-1],Sheet2!RC[-1]:R[1]C[-1],0),1)"
Cells(2,3).FormulaR1C1 = "=INDEX(Sheet2!R2C2:R3C3,MATCH(RC[-2],Sheet2!R2C1:R3C1,0),2)"
Then you can find the last row in the ID column on sheet 1, and fill the formula down both of the columns. Once the formula has been filled down, just copy and paste as values.
Dim lstRow As Long
lstRow = Sheets("Sheet 1").Cells(Rows.Count, 1).End(xlUp).Row '' find last row
Range(Cells(2, 2), Cells(lstRow, 3)).FillDown
Range(Cells(2, 2), Cells(lstRow, 3)).Copy
Cells(2, 2).PasteSpecial Paste:=xlPasteValues
Edit: You can use the lstRow variable within the VBA formula to make sure the formula is covering the whole range everytime the automation is run. You can use the 'Record Macro' button within excel to get the code for a formula, if you are not comfortable creating them yourself.
The Problem with your code is that
Set rngA = Range("A2" & lastrowA)
evaluates to Range("A25") for lastRowA=5.
If you want to address multiple cells, use
Set rngA = Range("A2:A" & lastrowA)
to get Range("A2:A5") for lastRowA = 5.
Besides that, formulas as already mentioned are an elegant solution as well.

Select and extract row of data to another sheet

I'm working with big worksheet containing stocks information, with columns organized like this :
ID DATE TIME PRICE QUANTITY NBE
It goes on for 500k+ rows, and I have 10+ sheets to go through. I need to extract only the first two trade of each trading day, and create a new list on a new sheet (Sheet1). The first trade of every day is always at "09:00:00".
So far I wrote this piece of code, in which I tried to copy the two lines I need and then paste them into Sheet1 thus creating the new list. It runs without errors, but nothing shows up...
Sub Macro1()
i = 2
Range("C2").Select
Range(Selection, Selection.End(xlDown)).Select
For Each Cell In Selection
If Day(.Range("B" & cRow).Value) <> Day(.Range("B" & cRow - 1).Value) Then
ActiveCell.EntireRow.Copy
ActiveWorkbook.Sheets("Sheet1").Rows(i).Paste
ActiveCell.Offset(1).Copy
ActiveWorkbook.Sheets("Sheet1").Rows(i + 1).Paste
i = i + 2
End If
Next Cell
End Sub
Shouldn't i select and the copy paste the two rows together? Or is it possible to create a range consisting of 2 rows and 6 columns from the activecell and then copy paste that range?
EDIT 1: It's not working.. I updated it like above, but I still get an error 438 here ActiveWorkbook.Sheets("Sheet1").Rows(i).Paste
EDIT 2: I'm def a big noob. Just realized not every first trade was made at 9:00:00 so i need to select the row based on wether or not one day have passed, and select the first two.
Can I use this condition instead : If Day(Range("B" & cRow).Value) <> Day(Range("B" & cRow - 1).Value) Then ?
I'm betting that your Time column is formatted as a Date/Time field, so you're comparing a string 09:00:00 to a long (date/time) and it's never going to be equal.
Try this:
if Format(Cell.Value, "hh:mm:ss") = "09:00:00" Then
And your English isn't bad at all...
This should do it quickly
make sure your on the sheet with data and run it, and it will copy it onto sheet1 in the same workbook starting at row2
you should make sure sheet1 is empty also , with .clearContents
Sub Macro1()
Dim lngFirstRow As Long
Dim lngLastRow As Long
Dim cRow As Long
Dim shSrc As Worksheet
Dim lngNextDestRow As Long
Dim shDest As Worksheet
Application.ScreenUpdating = False
Set shSrc = ActiveWorkbook.ActiveSheet
Set shDest = ActiveWorkbook.Sheets("Sheet1")
With shSrc
lngFirstRow = 2
lngLastRow = .Cells.Find(What:="*", After:=.Cells.Cells(1), LookAt:=xlPart, LookIn:=xlFormulas, SearchOrder:=xlByRows, SearchDirection:=xlPrevious, MatchCase:=False).Row
lngNextDestRow = 2
For cRow = lngFirstRow To lngLastRow Step 1
If Format(.Range("C" & cRow).value, "hh:mm:ss") = "09:00:00" Then
.Rows(cRow).EntireRow.Copy Destination:=shDest.Range("A" & lngNextDestRow )
.Rows(cRow+1).EntireRow.Copy Destination:=shDest.Range("A" & lngNextDestRow+1 )
lngNextDestRow = lngNextDestRow + 2
End If
Next cRow
End With
Application.ScreenUpdating = True
End Sub
When you refrence a sheet using the following line
ActiveWorkbook.Sheets(Sheet1).Rows(i).Paste
Sheet1 is likely a variable that is not defined properly. If "Sheet1" is the actual name of the sheet then enclose it in doublequotes
ActiveWorkbook.Sheets("Sheet1").Rows(i).Paste
After looking at #FreeMan's answer....you should do that first. You'll probably get an error 9 subscript error after you fix what he said to do.

Broken VBA Loop

I'm sure this is simple I just can't find anything on the Web.
I'm writing a Macro to format XL spreadsheets that i download from a 3rd party application. They come formatted all wacky so i'm trying to make it easier to get the data we need from them.
This is a simple VBA Do Loop that causes the cells in Column BL to update. The data in these cells contain line breaks which don't show up until you double click in the cell. The VBA below causes an update to the cells which achieves the same effect, just with less work. However it is currently crashing excel and I can't figure out why. It works in a single instance, but when I loop -- BOOM!!! -- frozen. Any help would be gently appreciated.
Sub updateCell()
Dim currentValue As String
ActiveSheet.Range("BL1").Select
Do Until ActiveCell.Value = ""
ActiveCell.Offset(1, 0).Select
currentValue = ActiveCell().Value
ActiveCell().Value = currentValue & ""
Loop
End Sub
Try something a bit more direct:
With ActiveSheet
lrow = .Range("BL" & .Rows.Count).End(xlUp).Row '~~> find last row on BL
With .Range("BL1:BL" & lrow) '~~> work on the target range
.Value = .Value '~~> assign its current value to it
End With
End With
Above code is like manually pressing F2 then pressing Enter.
Edit1: Explanation on getting the last row
ActiveSheet.Rows.Count '~~> Returns the number of rows in a sheet which is 1048576
MsgBox ActiveSheet.Rows.Count '~~> run this to confirm
So this line actually concatenates BL to 1048576.
.Range("BL" & .Rows.Count) '~~> Count is a property of the Rows Collection
Same as:
.Range("BL" & 1048576)
And same as:
.Range("BL1048576")
Then to get to the last row, we use Range Object End Method.
.Range("BL" & .Rows.Count).End(xlUp)
So basically, above code go to Cell BL1048576 then like manually pressing Ctrl+Arrow Up.
To return the actual row number of the range, we use the Range Object Row property.
lrow = .Range("BL" & .Rows.Count).End(xlUp).Row
See here more about With Statement.
It has the same effect (with your code) without the loop. HTH
But if what you want is to remove Line Breaks produced by Alt+Enter on a cell, try below:
Dim lrow As Long, c As Range
With ActiveSheet
lrow = .Range("BL" & .Rows.Count).End(xlUp).Row
For Each c In .Range("BL1:BL" & lrow)
c.Value = Replace(c.Value, Chr(10), "")
Next
End With
Where Chr(10) is the equivalent of Line Break replaced with "" using Replace Function.

Select Method of Worksheet Class Failed

I have this sub in Excel 2010 which is supposed to filter through all the cells in a sheet until it finds a match to Proj No, then paste a field from this row into another field.
When I try to run the sub, it gives me an error 1004: Select Method of Worksheet Class Failed. I've marked the line where this occurs. Any assistance would be greatly appreciated.
Option Explicit
Private Sub btnNext_Click()
Dim ProjNo As String
Dim Col As String
Dim Row As String
Dim cell As Range
Unload Dialog
formWait.Show
Sheets("Sheet7").Activate
ProjNo = Worksheets("Sheet1").Range("D6").Value
Col = Cells(Rows.Count, "A").End(xlUp).Row
For Each cell In Range("A2:A" & Col) 
If cell.Value = ProjNo Then
Row = Row & cell.Row
End If
Next cell
Workbooks("Form.xlsm").Sheets("Sheet7").Range("Row, 6").Copy Destination:=Sheets("Sheet1").Range("19, 5") ‘Error
Unload formWait
End Sub
I don't know what GWP is, but I think you want to use ProjNo there. The Range property doesn't accept an argument like that. Unless you have a named range of "Row,6" which you don't because it's not a legal name, you have to supply Range with a valid range reference, like A6 or D2:D12, for example.
Also, you can't concatenate rows and use them in a Range reference to get a larger range. You would have to copy each row inside the loop, union the ranges as you go, or better yet, filter on the value that you want and copy the visible rows.
Try this:
Private Sub btnNext_Click()
With ThisWorkbook.Worksheets("Sheet7")
'filter for the project id
.Range("A1", .Cells(.Rows.Count, 1).End(xlUp)).Resize(, 6).AutoFilter 1, "=" & .Range("D6").Value
'copy the visible rows
.Range("F2", .Cells(.Rows.Count, 6).End(xlUp)).SpecialCells(xlCellTypeVisible).Copy _
ThisWorkbook.Worksheets("Sheet1").Cells(19, 5)
'get rid of the filter
.AutoFilterMode = False
End With
End Sub
There are a few confusing items in your code above, so I wanted to place them long-form here. Let's get started:
Dim Col As String
Dim Row As String
It looks like your design expects these to be of type Long rather than type String. Even if these variables were meant to be strings, I would recommend adjusting their names -- when your fellow developer attempts to review your design, he or she is likely to see names like "Col" or "Row" and think "these are numbers". Easy fix:
Dim Col As Long, Row As Long
The next issue comes up here:
Col = Cells(Rows.Count, "A").End(xlUp).Row
The structure above is a common method for identifying the last ROW, not column. (It also appears that you have switched the "A" and number, which is another easy fix). While it is perfectly acceptable syntactically to name the variable for last row "Col", human users are likely to find this confusing. Identifying the last row (and the last col, which you use in the For Each loop), as explained in fantastic detail here, would be better handled like this:
Dim SheetSeven As Worksheet, SheetOne As Worksheet
Dim LastRow As Long, LastCol As Long
Set SheetSeven = ThisWorkbook.Worksheets("Sheet7")
Set SheetOne = ThisWorkbook.Worksheets("Sheet1")
With SheetSeven
LastRow = .Range("A" & .Rows.Count).End(xlUp).Row
LastCol = .Range("A" & .Columns.Count).End(xlToLeft).Column
End With
This should make your For Each loop look like this:
With SheetSeven
For Each cell in .Range("A2:A" & LastCol)
'... do you comparison and row incrementing here
Next cell
End With
Once you've identified your sheet as a variable, the Range.Copy action should be much easier as well:
With SheetSeven
.Range(.Cells(Row, 6)).Copy _
Destination:=SheetOne.Range(SheetOne.Cells(19, 5))
End With
Also one other thing you may wish to check is the status of Application.ScreenUpdating.
With the release of Office 2013 and later, a SDI (Single Document Interface) was introduced. If Application.ScreenUpdating is False and the workbook is not active, the implied call to Workbook.Activate will fail. Check the status of ScreenUpdating and set it to True if needed. You can set it back to False after the first Activate call for that workbook is made.
See this article:
https://support.microsoft.com/en-us/help/3083825/excel-workbook-is-not-activated-when-you-run-a-macro-that-calls-the-wo
In my case the error came as the sheet was hidden.
so I check if I am not working with the hidden sheet. Or you need to unhide the sheet before you try to select or activate sheet.
For Each sh In ThisWorkbook.Sheets
If Left(sh.Name, 8) <> "Template" Then
sh.Select
sh.Range("A1").Select
End If
Next