I have some code, can someone explain how it works and why it works that way? Specifically this line of code:
With Sheets("Sheet2")
.Cells(1, 1).End(xlDown).Offset(1).Value = Format(Date, "dd/mm/yyyy")
Now I get it that the code itself outputs the date in Cells(1,1) and offsets by 1 from the bottom for every subsequent entry. What I don't understand is why in the sheet itself one of the cells is blocked out?
And then the code works fine. But if cell A2 doesn't have '-------- it suddenly doesn't work anymore and gives me Application defined, object defined error. Can someone explain what '-------- does here and why its used?
The idea of the code is: "Jump to the last row that is used in column A, go down 1 row and write the date into it.
What it does:
Go (virtually) to cell A1 and press (also virtually) the key Ctrl+Down. This jumps to the last used cell of a column - but only if there is more than one cell filled, else it will jump to the very last row of the sheet (try this in Excel to understand).
Now if you offset one row from the very last row of a sheet, Excel cannot do anything more than complain.
The solution: Use .Cells(.rows.count, 1).End(xlUp) instead. This works the opposite way: Go to the very last row and press Ctrl+Up. For more details how to find the last used cell, see
Error in finding last used cell in Excel with VBA
What you also should do:
Don't cascade things, use intermediate variables, that helps debugging.
Don't write a date as string (format converts a date into a string). Write the date as date and set the number format of the cell.
With Sheets("Sheet2")
Dim lastCell as Range
Set lastCell = .Cells(.rows.count, 1).End(xlUp)
lastCell.Offset(1).Value = Date
' If needed:
' lastCell.Offset(1).NumberFormat = "dd/mm/yyyy"
End With
When there is no value in A2, it will give you an error because End(xlDown) from A1 will take you to the last cell A65536 in Excel 2003 and A1048576 in Excel 2007+. It is like selecting cell A1 and manually pressing the End key and then the Down arrow key. And of course you can't offset down from the last cell and hence you are getting the Error 1004 - Application-defined or Object-defined error.
I recommend using xlUp to find the last row and then enter the data as shown HERE
Here is an example
Dim lRow As Long
With Sheets("Sheet2")
lRow = .Range("A" & .Rows.Count).End(xlUp).Row + 1
.Cells(lRow, 1).Value = Format(Date, "dd/mm/yyyy")
End With
Related
Need a little help here.
In the "Data" Tab I want to copy values in column "c2:c1000" and paste in column "a1" of another Tab.
This is what i have so far,
Dim x As Long
Dim lastRow As Long
lastRow = Worksheet("Data").Cells(3, Columns.Count).End(xlUp).Column
For x = 1 To lastRow
If Worksheets("Sheet2").Cells(2, "A") = "" Then
Worksheets("Data").Range("c2:c1000").Copy Destination:=Worksheets("Sheet2").Range(1, "A")
Sheets("Sheet2").Range("A1").Value = Format(Now, "mm/dd/yyyy HH:mm:ss")
Else
Worksheets("Data").Range("c2:c1000").Copy Destination:=Worksheets("Sheet2").Cells(2,
Columns.Count).End(xlToLeft).Offset(, 1)
'Sheets("Sheet2").Range("A1").Value = Format(Now, "mm/dd/yyyy HH:mm:ss") --> can't figure how to increment this as this will need to be on the subsequent empty column
End If
Next
End Sub
Your help will be greatly appreciated!
Thank you.
Pasting values first into range A1 and down and then next time to cell B1 and so on, leaves no space for the timestamp to A1, B1 etc. So, I assume that you would like to paste the random values to row 2. So cells A1, B1, ... are left for the timestamp.
Inside the With statements we can refer to properties of the wsAudit so we can replace the "Worksheets("Audit")." reference with just "."
The column.count expression just checks the amount of columns in the worksheet.
The expression .Cells(2, Columns.Count) just points to last cell in the row 2.
The .End(xlToLeft).Column then looks from this column to left and is supposed to find the last not empty cell on this row. It's basically the same idea that in Excel's sheet you would go to cell XDF2 and hit CTRL+Arrow Left from keyboard.
But instead of activating the cell we just want to get the columns index number and then add 1 (the new column) and save it into variable. Now the new column is known.
The expression Range(.Cells(2, newColAudit), .Cells(1000, newColAudit)).Value is really the same as e.g. Range("B2:B1000"), but with this we can use the row and column index numbers instead. This is useful as the column number varies.
And as Samuel pointed out the copy paste operation can be avoided by setting the areas equal.
Dim wsAudit As Worksheet
Dim newColAudit As Long
Set wsAudit = Worksheets("Audit")
With wsAudit
newColAudit = .Cells(2, Columns.Count).End(xlToLeft).Column + 1
Range(.Cells(2, newColAudit), .Cells(1000, newColAudit)).Value = Worksheets("Data").Range("C2:C1000").Value
.Cells(1, newColAudit).Value = Format(Now, "mm/dd/yyyy HH:mm:ss")
End With
Much like your LastRow* variable for your source sheet, create a LastColumn variable for your destination sheet, which will find the last used column the same way you are finding your last used row.
Like so:
Dim LastColumn As Long
LastColumn = Sheets("Audit").Cells(1, Columns.Count).End(xlToLeft).Column
Then use the variable like so:
Destination:= Worksheets("Audit").Cells(1, LastColumn)
It seems that your code contradicts your question too, in your question you explained the data will be written to the Audit sheet in row 1, using the next column each time but your code looks for values in row 2 in your If statement:
If Worksheets("Audit").Cells(2, "A") = "" Then is the same as If Worksheets("Audit").Range("A2") = "" Then.
If you mean to check the first row, change the 2 to 1.
To help improve your codes efficiency:
(Also see the link to 'how to avoid select' in that question):
You can achieve 'copy/paste' without actually using the 'copy' and 'paste' methods by assigning the value of one range to the other, as example, like so:
Worksheets("Audit").Cells(1, LastColumn).Resize(999, 1) = Worksheets("Data").Range("c2:c1000").Value
Note: Change the Resize Property rows to suit the source range (in this case you are wanting to move values from C2:C1000).
*The LastRow variable is a bit confusing, as it is looking for the last used column in row 3.
If it's meant to find a column, consider renaming it to avoid confusion later on in debugging.
If it's meant to find the last row, try like this:
LastRow = Worksheet("Data").Cells(Rows.Count, 1).End(xlUp).Row
My issue now is with my conditional formatting rules - they are working as expected up to a certain point in column W, which is where the cells should be either highlighted or passed over. I have included a set of data from column W and column Z (the reference data) where there are matches that are not being highlighted. In the Column W data I have bolded the numbers that should be highlighted.
Column Z - Z506-Z550
233892
233899
959460
156311
515114
549794
562793
372953
230659
230717
2051205586
364834
790760
334588
538149
288261
19326
267428
Net 90
473853
3211221994
264556
260798
156271
509597
2211211506
800990
597593
431759
377289
224118
178966
276840
430269
431923
431986
547439
512399
234975
512203
602547
443537
376759
284287
608745
Column W - W1144-W1155
233892
367164
368384
344813
233899
233899
233895
-
233917
284287
376759
443537
The conditional formatting formula I have is =VLOOKUP($W4,$Z4:$Z922,1,FALSE) 'Applies To' =$W$4:$W$3600
I am not sure what is causing this conditional formatting to fail here.
Ignore below - now working with conditional formatting instead of vba
I am trying to automate a manual process of cross referencing data and highlighting a cell if the contents are found anywhere in another column of data. However, the amount of data in both of these columns is not the same. And unfortunately, the column I need to loop through and check each cell often has either blank cells or cells that are dashed ("---").
I started with conditional formatting but it was not working properly so I am now on VBA.
Private Sub Workbook_Open()
Dim LastRow As Long
Range("W4").Select
LastRow = Range("W4").End(xlDown).Row
Do Until ActiveCell.Row = LastRow
If Not IsEmpty(Application.Match(ActiveCell.Value, Range("Z:Z"), 0))
Then
ActiveCell.Interior.Color = vbGreen
ActiveCell.Offset(1, 0).Select
End If
Loop
End Sub
Right now the code has a couple issues. It is not finding the last row correctly - when debugging it shows as 65, but should be 3,535 in the test case I'm using. Additionally, my match statement is not working, as it is highlighting every cell instead of only those whose content is found in column Z. And, it highlights up to row 410, which means my Do Until loop must be wrong as well.
After figuring out the one column I eventually need to allow checking columns AA and AB for content matches.
Thanks!
As mentioned in the comment, conditional formatting is the way to go. I just tried the following as a conditional format.
=VLOOKUP($A2,$C$2:$C$7,1,FALSE)
A column of numbers in A as the numbers to be cross-referenced, and a list in column C which are the numbers to be checked. It works perfectly.
I recommend to use Conditional Formatting. The following is just to explain what was wrong with your code:
Always define which worksheet you mean and avoid using Select in Excel VBA.
Using End(xlDown) will find the next free cell (not the last used). Instead use End(xlUp) from the very last cell of the worksheet.
Application.Match does not return a cell but a row number. Therefore IsEmpty does not work.
In the end something like this should work:
Dim ws As Worksheet
Set ws = ThisWorkbook.Worksheets("SheetName")
Dim LastRow As Long
LastRow = ws.Cells(ws.Rows.Count, "W").End(xlUp).Row
Dim MatchRow As Variant
Dim iRow As Long
For iRow = 4 To LastRow
MatchRow = 0 'initialize
MatchRow = Application.Match(ws.Cells(iRow, "W").Value, ws.Range("Z:Z"), 0)
If MatchRow > 0 Then
ws.Cells(iRow, "W").Interior.Color = vbGreen
End If
Next iRow
Note that Contidional Formatting would be a much better solution.
I have a column thats rows are currently filled with this formula:
=IF(VLOOKUP(H3,B:D,3,0)="NOT_FOUND","",VLOOKUP(H3,B:D,3,0))
That fills rows one by one with the value that it finds. I am hoping that there is a macro that will search the column (I) for the single last non-blank cell and convert the formula answer into a value so I can eventually sum all of the values. I assume it is not a very difficult macro, but I have no experience working with VBA so any help would be appreciated!
Here is a pic of part of the table I am trying to make. Where the 13.8 is I would like for that to be converted to just a value since it is the last non-blank cell in the column. Please let me know if this makes sense or if more info is needed. Thank you!
nofriendsnojo, if overwriting the formula in a cell (in this case VLOOKUP) is all you need, then please see my code below:
Sub paste_values()
Dim lastRow As Long
Dim cel As Range
'get last non-blank cell in column I
lastRow = Cells(Rows.Count, "I").End(xlUp).Row
'loop that overwrites cell contents with simple values
For Each cel In Range("I1:I" & lastRow)
cel.Copy
cel.PasteSpecial xlPasteValues
Next cel
Application.CutCopyMode = False
End Sub
This is the basic code, it copies values of the cell and then pastes them to the same cell as values. This ultimately gets rid of any formula and converts the result of the formula into a simple value. It is quite common practice.
Of course, you can then add some references like ThisWorkbook. or ActiveSheet. or whatever scope you need.
I hope this solves your issue or atleast directs you in the right way.
I'm using VBA code to write to cells in excel. For eg.
Range("C3") = code
Or
Cells(3,3) = code
If a row is inserted in the sheet, the code does then not update accordingly and would still write to Range("C3") etc. So the code then writes to the incorrect cell.
Is there a better way I can structure my code so it will update accordingly? Perhaps using a table instead of cells?
One solution is to used Named Ranges. You can define a Named Range in Formula Tab by clicking on Name Manager.
Then you will write.
Range("Name of the Range") = code
My believe is that named ranges update automatically when a row or column is inserted, so your code will print the value in the correct cell.
Thanks, good idea. I ended up defining the column filled with values as a range, then use a for loop to search for the defined string. That way it doesnt matter what row it is in aslong as the name and string doesnt change (Using a Named Range will probably be better for that exact reason).
Worksheets("Sheet1").Select
Set WS = ActiveSheet
With WS
LastRow = .Cells(.Rows.Count, 2).End(xlUp).Row 'Determine the last row number with data in it for column B
For Each acell In .Range("B1:B" & LastRow) 'Defines the search range from B1 to last row
If acell.Value = "String Searched For" Then
'Do stuff based on found cell location
End If
If acell.Value = "String Searched For#2" Then
'Do stuff based on found cell location#2
End If
Next
End With
Hello from an unexperienced vba user.. I'm having trouble with the code for autofill to the last row when trying to use named ranges for the column. Everything seems to work fine when I use a hard coding for the column, in this case Column CW, and what I need is to replace this column CW with a named range so that the macro will still work when adding or deleting columns in the worksheet.
I used the following named ranges:
First_Date: This is the header cell of one of the columns (in this case AP5)
Second_Row: This is the range of columns I want to copy the formulas from (AP7:CW7)
Second_Cell: The cell where I want to start to autofill (AP7)
Last_Column: This is column CW that I want to use in the code. Autofill would be up to this column and down to the last row.
After searching in different threads, I came up with the following code that seems to work fine. How can I change column CW to a named range? Or do I need to change the code?
Dim Lr As Integer
Lr = Range("First_Date").End(xlDown).Row 'Searching last row
Rows(Lr).Insert Shift:=xlDown 'Inserting new row
Range("Second_Row").AutoFill Destination:=Range(Range("Second_Cell"), Range("CW" & Lr))
Can anyone assist me here please?
This will get the job done :-)
Sub RangerFiller()
'The Cell that holds the formula B1
OriginalFormula = Cells(1, 2).Formula
'copies formula down to the last column next to B but use can use another column as
'a counting column....the column that hold the last value
Range("B2:B" & Cells(Rows.Count, "A").End(xlUp).Row).Formula = OriginalFormula
End Sub
Someone gave me the solution:
Change
Range("CW" & Lr)
To
Cells(Lr, Range("Last_Column").Column)
I faced a similar problem because I don't want to hard code the cell reference. I found this solution below to be useful, by using "& ______ &" to replace the cell number that can be calculated using input box or formula.
Eg.
cell1 = last row of column A
Range("CW " & cell1 &" :CW & Lr),
where cell1 = any number that can be added via input box/formula.
Hope this helps!